authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-06 20:44:51+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-06 20:44:51+01:00
log27c084065abcc404b7f58562f802999ae3ebce10
tree3979a57d5ed16c8dc76534e43fbcddf373c4e2db
parent9154a8606996ce34e5f1d805672c83e2b733f5a7
parent13fca53b925e7de00b63efbf6ac3723a4df732a8
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11070 from Luukdegram/wasm-unify

stage2: wasm - unify codegen with other backends

11 files changed, 146 insertions(+), 528 deletions(-)

src/arch/wasm/CodeGen.zig+46-439
......@@ -8,6 +8,7 @@ const mem = std.mem;
88const wasm = std.wasm;
99const log = std.log.scoped(.codegen);
1010
11const codegen = @import("../../codegen.zig");
1112const Module = @import("../../Module.zig");
1213const Decl = Module.Decl;
1314const Type = @import("../../type.zig").Type;
......@@ -546,7 +547,7 @@ blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct {
546547 value: WValue,
547548}) = .{},
548549/// `bytes` contains the wasm bytecode belonging to the 'code' section.
549code: ArrayList(u8),
550code: *ArrayList(u8),
550551/// The index the next local generated will have
551552/// NOTE: arguments share the index with locals therefore the first variable
552553/// will have the index that comes after the last argument's index
......@@ -566,9 +567,6 @@ locals: std.ArrayListUnmanaged(u8),
566567target: std.Target,
567568/// Represents the wasm binary file that is being linked.
568569bin_file: *link.File.Wasm,
569/// Reference to the Module that this decl is part of.
570/// Used to find the error value.
571module: *Module,
572570/// List of MIR Instructions
573571mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
574572/// Contains extra data for MIR
......@@ -611,7 +609,6 @@ pub fn deinit(self: *Self) void {
611609 self.locals.deinit(self.gpa);
612610 self.mir_instructions.deinit(self.gpa);
613611 self.mir_extra.deinit(self.gpa);
614 self.code.deinit();
615612 self.* = undefined;
616613}
617614
......@@ -639,7 +636,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
639636 }
640637
641638 // 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
643640 // to the 'rodata' section. We then return the index into the section as `WValue`.
644641 //
645642 // 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 {
822819 };
823820}
824821
825pub fn genFunc(self: *Self) InnerError!void {
822pub 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
855fn genFunc(self: *Self) InnerError!void {
826856 var func_type = try genFunctype(self.gpa, self.decl.ty, self.target);
827857 defer func_type.deinit(self.gpa);
828858 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 {
889919 var emit: Emit = .{
890920 .mir = mir,
891921 .bin_file = &self.bin_file.base,
892 .code = &self.code,
922 .code = self.code,
893923 .locals = self.locals.items,
894924 .decl = self.decl,
895925 };
......@@ -903,433 +933,6 @@ pub fn genFunc(self: *Self) InnerError!void {
903933 };
904934}
905935
906pub 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
1333936const CallWValues = struct {
1334937 args: []WValue,
1335938 return_value: WValue,
......@@ -1809,8 +1412,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
18091412
18101413 if (func_val.castTag(.function)) |func| {
18111414 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;
18141421 } else if (func_val.castTag(.decl_ref)) |decl_ref| {
18151422 break :blk decl_ref.data;
18161423 }
......@@ -2184,7 +1791,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
21841791 },
21851792 .ErrorSet => switch (val.tag()) {
21861793 .@"error" => {
2187 const kv = try self.module.getErrorValue(val.getError().?);
1794 const kv = try self.bin_file.base.options.module.?.getErrorValue(val.getError().?);
21881795 return WValue{ .imm32 = kv.value };
21891796 },
21901797 else => return WValue{ .imm32 = 0 },
......@@ -2275,7 +1882,7 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
22751882 .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt())),
22761883 },
22771884 .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
22791886 return @bitCast(i32, kv.value);
22801887 },
22811888 else => unreachable, // Programmer called this function for an illegal type
src/codegen.zig+3-2
......@@ -83,8 +83,6 @@ pub fn generateFunction(
8383 debug_output: DebugInfoOutput,
8484) GenerateSymbolError!FnResult {
8585 switch (bin_file.options.target.cpu.arch) {
86 .wasm32 => unreachable, // has its own code path
87 .wasm64 => unreachable, // has its own code path
8886 .arm,
8987 .armeb,
9088 => return @import("arch/arm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
......@@ -136,6 +134,9 @@ pub fn generateFunction(
136134 //.renderscript32 => return Function(.renderscript32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
137135 //.renderscript64 => return Function(.renderscript64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
138136 //.ve => return Function(.ve).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
137 .wasm32,
138 .wasm64,
139 => return @import("arch/wasm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
139140 else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
140141 }
141142}
src/link.zig+1-1
......@@ -702,7 +702,7 @@ pub const File = struct {
702702 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl, reloc_info),
703703 .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl, reloc_info),
704704 .c => unreachable,
705 .wasm => unreachable,
705 .wasm => return @fieldParentPtr(Wasm, "base", base).getDeclVAddr(decl, reloc_info),
706706 .spirv => unreachable,
707707 .nvptx => unreachable,
708708 }
src/link/Wasm.zig+83-85
......@@ -14,6 +14,7 @@ const Atom = @import("Wasm/Atom.zig");
1414const Module = @import("../Module.zig");
1515const Compilation = @import("../Compilation.zig");
1616const CodeGen = @import("../arch/wasm/CodeGen.zig");
17const codegen = @import("../codegen.zig");
1718const link = @import("../link.zig");
1819const lldMain = @import("../main.zig").lldMain;
1920const trace = @import("../tracy.zig").trace;
......@@ -489,10 +490,8 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
489490 self.symbols.appendAssumeCapacity(symbol);
490491 }
491492
492 try self.resolved_symbols.putNoClobber(self.base.allocator, .{
493 .index = atom.sym_index,
494 .file = null,
495 }, {});
493 try self.resolved_symbols.putNoClobber(self.base.allocator, atom.symbolLoc(), {});
494 try self.symbol_atom.putNoClobber(self.base.allocator, atom.symbolLoc(), atom);
496495}
497496
498497pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
......@@ -505,33 +504,30 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
505504 const decl = func.owner_decl;
506505 assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes()
507506
508 decl.link.wasm.clear();
509
510 var codegen: CodeGen = .{
511 .gpa = self.base.allocator,
512 .air = air,
513 .liveness = liveness,
514 .values = .{},
515 .code = std.ArrayList(u8).init(self.base.allocator),
516 .decl = decl,
517 .err_msg = undefined,
518 .locals = .{},
519 .target = self.base.options.target,
520 .bin_file = self,
521 .module = module,
522 };
523 defer codegen.deinit();
507 decl.link.wasm.clear(self.base.allocator);
508
509 var code_writer = std.ArrayList(u8).init(self.base.allocator);
510 defer code_writer.deinit();
511 const result = try codegen.generateFunction(
512 &self.base,
513 decl.srcLoc(),
514 func,
515 air,
516 liveness,
517 &code_writer,
518 .none,
519 );
524520
525 // generate the 'code' section for the function declaration
526 codegen.genFunc() catch |err| switch (err) {
527 error.CodegenFail => {
521 const code = switch (result) {
522 .appended => code_writer.items,
523 .fail => |em| {
528524 decl.analysis = .codegen_failure;
529 try module.failed_decls.put(module.gpa, decl, codegen.err_msg);
525 try module.failed_decls.put(module.gpa, decl, em);
530526 return;
531527 },
532 else => |e| return e,
533528 };
534 return self.finishUpdateDecl(decl, codegen.code.items);
529
530 return self.finishUpdateDecl(decl, code);
535531}
536532
537533// Generate code for the Decl, storing it in memory to be later written to
......@@ -546,33 +542,39 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
546542
547543 assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes()
548544
549 decl.link.wasm.clear();
545 decl.link.wasm.clear(self.base.allocator);
546
547 if (decl.isExtern()) {
548 return self.addOrUpdateImport(decl);
549 }
550
551 if (decl.val.castTag(.function)) |_| {
552 return;
553 } else if (decl.val.castTag(.extern_fn)) |_| {
554 return;
555 }
556 const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
550557
551558 var code_writer = std.ArrayList(u8).init(self.base.allocator);
552559 defer code_writer.deinit();
553 var decl_gen: CodeGen.DeclGen = .{
554 .gpa = self.base.allocator,
555 .decl = decl,
556 .symbol_index = decl.link.wasm.sym_index,
557 .bin_file = self,
558 .err_msg = undefined,
559 .code = &code_writer,
560 .module = module,
561 };
562560
563 // generate the 'code' section for the function declaration
564 const result = decl_gen.genDecl() catch |err| switch (err) {
565 error.CodegenFail => {
561 const res = try codegen.generateSymbol(
562 &self.base,
563 decl.srcLoc(),
564 .{ .ty = decl.ty, .val = val },
565 &code_writer,
566 .none,
567 .{ .parent_atom_index = decl.link.wasm.sym_index },
568 );
569
570 const code = switch (res) {
571 .externally_managed => |x| x,
572 .appended => code_writer.items,
573 .fail => |em| {
566574 decl.analysis = .codegen_failure;
567 try module.failed_decls.put(module.gpa, decl, decl_gen.err_msg);
575 try module.failed_decls.put(module.gpa, decl, em);
568576 return;
569577 },
570 else => |e| return e,
571 };
572
573 const code = switch (result) {
574 .externally_managed => |data| data,
575 .appended => code_writer.items,
576578 };
577579
578580 return self.finishUpdateDecl(decl, code);
......@@ -603,7 +605,9 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 {
603605
604606 // Create and initialize a new local symbol and atom
605607 const local_index = decl.link.wasm.locals.items.len;
606 const name = try std.fmt.allocPrintZ(self.base.allocator, "__unnamed_{s}_{d}", .{ decl.name, local_index });
608 const fqdn = try decl.getFullyQualifiedName(self.base.allocator);
609 defer self.base.allocator.free(fqdn);
610 const name = try std.fmt.allocPrintZ(self.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index });
607611 defer self.base.allocator.free(name);
608612 var symbol: Symbol = .{
609613 .name = try self.string_table.put(self.base.allocator, name),
......@@ -625,36 +629,32 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 {
625629 atom.sym_index = @intCast(u32, self.symbols.items.len);
626630 self.symbols.appendAssumeCapacity(symbol);
627631 }
628 try self.resolved_symbols.putNoClobber(self.base.allocator, .{
629 .file = null,
630 .index = atom.sym_index,
631 }, {});
632 try self.resolved_symbols.putNoClobber(self.base.allocator, atom.symbolLoc(), {});
633 try self.symbol_atom.putNoClobber(self.base.allocator, atom.symbolLoc(), atom);
632634
633635 var value_bytes = std.ArrayList(u8).init(self.base.allocator);
634636 defer value_bytes.deinit();
635637
636638 const module = self.base.options.module.?;
637 var decl_gen: CodeGen.DeclGen = .{
638 .bin_file = self,
639 .decl = decl,
640 .err_msg = undefined,
641 .gpa = self.base.allocator,
642 .module = module,
643 .code = &value_bytes,
644 .symbol_index = atom.sym_index,
645 };
646
647 const result = decl_gen.genTypedValue(tv.ty, tv.val) catch |err| switch (err) {
648 error.CodegenFail => {
649 decl.analysis = .codegen_failure;
650 try module.failed_decls.put(module.gpa, decl, decl_gen.err_msg);
651 return error.AnalysisFail;
639 const result = try codegen.generateSymbol(
640 &self.base,
641 decl.srcLoc(),
642 tv,
643 &value_bytes,
644 .none,
645 .{
646 .parent_atom_index = atom.sym_index,
647 .addend = null,
652648 },
653 else => |e| return e,
654 };
649 );
655650 const code = switch (result) {
651 .externally_managed => |x| x,
656652 .appended => value_bytes.items,
657 .externally_managed => |data| data,
653 .fail => |em| {
654 decl.analysis = .codegen_failure;
655 try module.failed_decls.put(module.gpa, decl, em);
656 return error.AnalysisFail;
657 },
658658 };
659659
660660 atom.size = @intCast(u32, code.len);
......@@ -666,35 +666,31 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 {
666666/// Returns the given pointer address
667667pub fn getDeclVAddr(
668668 self: *Wasm,
669 decl: *Module.Decl,
670 symbol_index: u32,
671 target_decl: *Module.Decl,
672 offset: u32,
673 addend: u32,
674) !u32 {
675 const target_symbol_index = target_decl.link.wasm.sym_index;
669 decl: *const Module.Decl,
670 reloc_info: link.File.RelocInfo,
671) !u64 {
672 const target_symbol_index = decl.link.wasm.sym_index;
676673 assert(target_symbol_index != 0);
677 assert(symbol_index != 0);
678
679 const atom = decl.link.wasm.symbolAtom(symbol_index);
674 assert(reloc_info.parent_atom_index != 0);
675 const atom = self.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?;
680676 const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;
681 if (target_decl.ty.zigTypeTag() == .Fn) {
682 assert(addend == 0); // addend not allowed for function relocations
677 if (decl.ty.zigTypeTag() == .Fn) {
678 assert(reloc_info.addend == 0); // addend not allowed for function relocations
683679 // We found a function pointer, so add it to our table,
684680 // as function pointers are not allowed to be stored inside the data section.
685681 // They are instead stored in a function table which are called by index.
686682 try self.addTableFunction(target_symbol_index);
687683 try atom.relocs.append(self.base.allocator, .{
688684 .index = target_symbol_index,
689 .offset = offset,
685 .offset = @intCast(u32, reloc_info.offset),
690686 .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
691687 });
692688 } else {
693689 try atom.relocs.append(self.base.allocator, .{
694690 .index = target_symbol_index,
695 .offset = offset,
691 .offset = @intCast(u32, reloc_info.offset),
696692 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
697 .addend = addend,
693 .addend = reloc_info.addend,
698694 });
699695 }
700696 // we do not know the final address at this point,
......@@ -824,12 +820,14 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
824820 local_symbol.tag = .dead; // also for any local symbol
825821 self.symbols_free_list.append(self.base.allocator, local_atom.sym_index) catch {};
826822 assert(self.resolved_symbols.swapRemove(local_atom.symbolLoc()));
823 assert(self.symbol_atom.remove(local_atom.symbolLoc()));
827824 }
828825
829826 if (decl.isExtern()) {
830827 assert(self.imports.remove(atom.symbolLoc()));
831828 }
832829 assert(self.resolved_symbols.swapRemove(atom.symbolLoc()));
830 assert(self.symbol_atom.remove(atom.symbolLoc()));
833831 atom.deinit(self.base.allocator);
834832}
835833
......@@ -989,7 +987,7 @@ fn allocateAtoms(self: *Wasm) !void {
989987 atom.size,
990988 });
991989 offset += atom.size;
992 try self.symbol_atom.putNoClobber(self.base.allocator, symbol_loc, atom);
990 self.symbol_atom.putAssumeCapacity(atom.symbolLoc(), atom); // Update atom pointers
993991 atom = atom.next orelse break;
994992 }
995993 }
src/link/Wasm/Atom.zig+6-1
......@@ -62,9 +62,14 @@ pub fn deinit(self: *Atom, gpa: Allocator) void {
6262
6363/// Sets the length of relocations and code to '0',
6464/// effectively resetting them and allowing them to be re-populated.
65pub fn clear(self: *Atom) void {
65pub fn clear(self: *Atom, gpa: Allocator) void {
6666 self.relocs.clearRetainingCapacity();
6767 self.code.clearRetainingCapacity();
68
69 // locals will be re-generated
70 for (self.locals.items) |*local| {
71 local.deinit(gpa);
72 }
6873}
6974
7075pub fn format(self: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
src/link/Wasm/Object.zig+1
......@@ -861,6 +861,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
861861 }
862862
863863 try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);
864 try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom);
864865
865866 const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index];
866867 segment.alignment = std.math.max(segment.alignment, atom.alignment);
test/behavior/align.zig+1
......@@ -149,6 +149,7 @@ test "return error union with 128-bit integer" {
149149 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
150150 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
151151 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
152 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
152153
153154 try expect(3 == try give());
154155}
test/behavior/bugs/7250.zig+1
......@@ -18,5 +18,6 @@ test "reference a global threadlocal variable" {
1818 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1919 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
2020 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
21 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
2122 _ = nrfx_uart_rx(&g_uart0);
2223}
test/behavior/cast.zig+2
......@@ -1060,6 +1060,7 @@ test "compile time int to ptr of function" {
10601060 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10611061 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10621062 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1063 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
10631064
10641065 try foobar(FUNCTION_CONSTANT);
10651066}
......@@ -1141,6 +1142,7 @@ test "cast u128 to f128 and back" {
11411142 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
11421143 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11431144 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1145 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
11441146
11451147 comptime try testCast128();
11461148 try testCast128();
test/behavior/slice.zig+1
......@@ -209,6 +209,7 @@ test "compile time slice of pointer to hard coded address" {
209209 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
210210 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
211211 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
212 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
212213
213214 try expect(@ptrToInt(x) == 0x1000);
214215 try expect(x.len == 0x500);
test/behavior/struct.zig+1
......@@ -857,6 +857,7 @@ test "non-packed struct with u128 entry in union" {
857857 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
858858 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
859859 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
860 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
860861
861862 const U = union(enum) {
862863 Num: u128,