authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-18 21:43:20+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-19 20:01:23+01:00
log1fe1e4d29207de8c972fcc1b61266fbaddb07d63
tree1f5b8756eab0056a2f6c3cdf05a7bdbfc977fc3b
parent28acbdb02ff934fed3363a580128575e7d8c92ee
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Split funcgen and declgen

This allows us to get rid of unused fields when generating code for non-function decls. We can now create seperate instances of `DeclGen` which in turn can then be used to generate the code for a constant. Besides those reasons, it will be much easier to switch to the generic purpose `codegen.zig` that any backend should use. Allowing us to deduplicate this code.

3 files changed, 363 insertions(+), 312 deletions(-)

src/arch/wasm/CodeGen.zig+290-287
...@@ -542,10 +542,9 @@ locals: std.ArrayListUnmanaged(u8),...@@ -542,10 +542,9 @@ locals: std.ArrayListUnmanaged(u8),
542target: std.Target,542target: std.Target,
543/// Represents the wasm binary file that is being linked.543/// Represents the wasm binary file that is being linked.
544bin_file: *link.File.Wasm,544bin_file: *link.File.Wasm,
545/// Table with the global error set. Consists of every error found in545/// Reference to the Module that this decl is part of.
546/// the compiled code. Each error name maps to a `Module.ErrorInt` which is emitted546/// Used to find the error value.
547/// during codegen to determine the error value.547module: *Module,
548global_error_set: std.StringHashMapUnmanaged(Module.ErrorInt),
549/// List of MIR Instructions548/// List of MIR Instructions
550mir_instructions: std.MultiArrayList(Mir.Inst) = .{},549mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
551/// Contains extra data for MIR550/// Contains extra data for MIR
...@@ -581,7 +580,7 @@ pub fn deinit(self: *Self) void {...@@ -581,7 +580,7 @@ pub fn deinit(self: *Self) void {
581 self.* = undefined;580 self.* = undefined;
582}581}
583582
584/// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig583/// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig
585fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError {584fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError {
586 const src: LazySrcLoc = .{ .node_offset = 0 };585 const src: LazySrcLoc = .{ .node_offset = 0 };
587 const src_loc = src.toSrcLoc(self.decl);586 const src_loc = src.toSrcLoc(self.decl);
...@@ -674,50 +673,41 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) error{OutOfMemory}!u32 {...@@ -674,50 +673,41 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) error{OutOfMemory}!u32 {
674 return result;673 return result;
675}674}
676675
677/// Using a given `Type`, returns the corresponding wasm Valtype676/// Using a given `Type`, returns the corresponding type
678fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {677fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {
679 return switch (ty.zigTypeTag()) {678 return switch (ty.zigTypeTag()) {
680 .Float => blk: {679 .Float => blk: {
681 const bits = ty.floatBits(self.target);680 const bits = ty.floatBits(target);
682 if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32;681 if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32;
683 if (bits == 64) break :blk wasm.Valtype.f64;682 if (bits == 64) break :blk wasm.Valtype.f64;
684 return self.fail("Float bit size not supported by wasm: '{d}'", .{bits});683 return wasm.Valtype.i32; // represented as pointer to stack
685 },684 },
686 .Int => blk: {685 .Int => blk: {
687 const info = ty.intInfo(self.target);686 const info = ty.intInfo(target);
688 if (info.bits <= 32) break :blk wasm.Valtype.i32;687 if (info.bits <= 32) break :blk wasm.Valtype.i32;
689 if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64;688 if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64;
690 break :blk wasm.Valtype.i32; // represented as pointer to stack689 break :blk wasm.Valtype.i32; // represented as pointer to stack
691 },690 },
692 .Enum => switch (ty.tag()) {691 .Enum => switch (ty.tag()) {
693 .enum_simple => wasm.Valtype.i32,692 .enum_simple => wasm.Valtype.i32,
694 else => self.typeToValtype(ty.cast(Type.Payload.EnumFull).?.data.tag_ty),693 else => typeToValtype(ty.cast(Type.Payload.EnumFull).?.data.tag_ty, target),
695 },694 },
696 .Bool,695 else => wasm.Valtype.i32, // all represented as reference/immediate
697 .Pointer,
698 .ErrorSet,
699 .Struct,
700 .ErrorUnion,
701 .Optional,
702 .Fn,
703 .Array,
704 => wasm.Valtype.i32,
705 else => self.fail("TODO - Wasm typeToValtype for type '{}'", .{ty}),
706 };696 };
707}697}
708698
709/// Using a given `Type`, returns the byte representation of its wasm value type699/// Using a given `Type`, returns the byte representation of its wasm value type
710fn genValtype(self: *Self, ty: Type) InnerError!u8 {700fn genValtype(ty: Type, target: std.Target) u8 {
711 return wasm.valtype(try self.typeToValtype(ty));701 return wasm.valtype(typeToValtype(ty, target));
712}702}
713703
714/// Using a given `Type`, returns the corresponding wasm value type704/// Using a given `Type`, returns the corresponding wasm value type
715/// Differently from `genValtype` this also allows `void` to create a block705/// Differently from `genValtype` this also allows `void` to create a block
716/// with no return type706/// with no return type
717fn genBlockType(self: *Self, ty: Type) InnerError!u8 {707fn genBlockType(ty: Type, target: std.Target) u8 {
718 return switch (ty.tag()) {708 return switch (ty.tag()) {
719 .void, .noreturn => wasm.block_empty,709 .void, .noreturn => wasm.block_empty,
720 else => self.genValtype(ty),710 else => genValtype(ty, target),
721 };711 };
722}712}
723713
...@@ -739,7 +729,7 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void {...@@ -739,7 +729,7 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void {
739/// Returns a corresponding `Wvalue` with `local` as active tag729/// Returns a corresponding `Wvalue` with `local` as active tag
740fn allocLocal(self: *Self, ty: Type) InnerError!WValue {730fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
741 const initial_index = self.local_index;731 const initial_index = self.local_index;
742 const valtype = try self.genValtype(ty);732 const valtype = genValtype(ty, self.target);
743 try self.locals.append(self.gpa, valtype);733 try self.locals.append(self.gpa, valtype);
744 self.local_index += 1;734 self.local_index += 1;
745 return WValue{ .local = initial_index };735 return WValue{ .local = initial_index };
...@@ -747,33 +737,33 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue {...@@ -747,33 +737,33 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
747737
748/// Generates a `wasm.Type` from a given function type.738/// Generates a `wasm.Type` from a given function type.
749/// Memory is owned by the caller.739/// Memory is owned by the caller.
750fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {740fn genFunctype(gpa: Allocator, fn_ty: Type, target: std.Target) !wasm.Type {
751 var params = std.ArrayList(wasm.Valtype).init(self.gpa);741 var params = std.ArrayList(wasm.Valtype).init(gpa);
752 defer params.deinit();742 defer params.deinit();
753 var returns = std.ArrayList(wasm.Valtype).init(self.gpa);743 var returns = std.ArrayList(wasm.Valtype).init(gpa);
754 defer returns.deinit();744 defer returns.deinit();
755 const return_type = fn_ty.fnReturnType();745 const return_type = fn_ty.fnReturnType();
756746
757 const want_sret = self.isByRef(return_type);747 const want_sret = isByRef(return_type, target);
758748
759 if (want_sret) {749 if (want_sret) {
760 try params.append(try self.typeToValtype(Type.usize));750 try params.append(typeToValtype(return_type, target));
761 }751 }
762752
763 // param types753 // param types
764 if (fn_ty.fnParamLen() != 0) {754 if (fn_ty.fnParamLen() != 0) {
765 const fn_params = try self.gpa.alloc(Type, fn_ty.fnParamLen());755 const fn_params = try gpa.alloc(Type, fn_ty.fnParamLen());
766 defer self.gpa.free(fn_params);756 defer gpa.free(fn_params);
767 fn_ty.fnParamTypes(fn_params);757 fn_ty.fnParamTypes(fn_params);
768 for (fn_params) |param_type| {758 for (fn_params) |param_type| {
769 if (!param_type.hasCodeGenBits()) continue;759 if (!param_type.hasCodeGenBits()) continue;
770 try params.append(try self.typeToValtype(param_type));760 try params.append(typeToValtype(param_type, target));
771 }761 }
772 }762 }
773763
774 // return type764 // return type
775 if (!want_sret and return_type.hasCodeGenBits()) {765 if (!want_sret and return_type.hasCodeGenBits()) {
776 try returns.append(try self.typeToValtype(return_type));766 try returns.append(typeToValtype(return_type, target));
777 }767 }
778768
779 return wasm.Type{769 return wasm.Type{
...@@ -782,8 +772,8 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {...@@ -782,8 +772,8 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {
782 };772 };
783}773}
784774
785pub fn genFunc(self: *Self) InnerError!Result {775pub fn genFunc(self: *Self) InnerError!void {
786 var func_type = try self.genFunctype(self.decl.ty);776 var func_type = try genFunctype(self.gpa, self.decl.ty, self.target);
787 defer func_type.deinit(self.gpa);777 defer func_type.deinit(self.gpa);
788 self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);778 self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
789779
...@@ -828,240 +818,260 @@ pub fn genFunc(self: *Self) InnerError!Result {...@@ -828,240 +818,260 @@ pub fn genFunc(self: *Self) InnerError!Result {
828 },818 },
829 else => |e| return e,819 else => |e| return e,
830 };820 };
831
832 // codegen data has been appended to `code`
833 return Result.appended;
834}821}
835822
836pub fn genDecl(self: *Self) InnerError!Result {823pub const DeclGen = struct {
837 const decl = self.decl;824 /// The decl we are generating code for.
838 assert(decl.has_tv);825 decl: *Decl,
839826 /// The symbol we're generating code for.
840 log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val });827 /// This can either be the symbol of the Decl itself,
841828 /// or one of its locals.
842 if (decl.val.castTag(.function)) |func_payload| {829 symbol_index: u32,
843 _ = func_payload;830 gpa: Allocator,
844 return self.fail("TODO wasm backend genDecl function pointer", .{});831 /// A reference to the linker, that will process the decl's
845 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {832 /// code and create any relocations it deems neccesary.
846 const ext_decl = extern_fn.data;833 bin_file: *link.File.Wasm,
847 var func_type = try self.genFunctype(ext_decl.ty);834 /// This will be set when `InnerError` has been returned.
848 func_type.deinit(self.gpa);835 /// In any other case, this will be 'undefined'.
849 ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);836 err_msg: *Module.ErrorMsg,
850 return Result.appended;837 /// Reference to the Module that is being compiled.
851 } else {838 /// Used to find the error value of an error.
852 const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {839 module: *Module,
853 break :init_val payload.data.init;840 /// The list of bytes that have been generated so far,
854 } else decl.val;841 /// can be used to calculate the offset into a section.
855 if (init_val.tag() != .unreachable_value) {842 code: *std.ArrayList(u8),
856 return try self.genTypedValue(decl.ty, init_val);843
844 /// Sets `err_msg` on `DeclGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig
845 fn fail(self: *DeclGen, comptime fmt: []const u8, args: anytype) InnerError {
846 const src: LazySrcLoc = .{ .node_offset = 0 };
847 const src_loc = src.toSrcLoc(self.decl);
848 self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args);
849 return error.CodegenFail;
850 }
851
852 fn target(self: *const DeclGen) std.Target {
853 return self.bin_file.base.options.target;
854 }
855
856 pub fn genDecl(self: *DeclGen) InnerError!Result {
857 const decl = self.decl;
858 assert(decl.has_tv);
859
860 log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val });
861
862 if (decl.val.castTag(.function)) |func_payload| {
863 _ = func_payload;
864 return self.fail("TODO wasm backend genDecl function pointer", .{});
865 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {
866 const ext_decl = extern_fn.data;
867 var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target());
868 func_type.deinit(self.gpa);
869 ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
870 return Result.appended;
871 } else {
872 const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
873 break :init_val payload.data.init;
874 } else decl.val;
875 if (init_val.tag() != .unreachable_value) {
876 return try self.genTypedValue(decl.ty, init_val, self.code.writer());
877 }
878 return Result.appended;
857 }879 }
858 return Result.appended;
859 }880 }
860}
861881
862/// Generates the wasm bytecode for the declaration belonging to `Context`882 /// Generates the wasm bytecode for the declaration belonging to `Context`
863fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result {883 fn genTypedValue(self: *DeclGen, ty: Type, val: Value, writer: anytype) InnerError!Result {
864 if (val.isUndef()) {884 if (val.isUndef()) {
865 try self.code.appendNTimes(0xaa, @intCast(usize, ty.abiSize(self.target)));885 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
866 return Result.appended;886 return Result.appended;
867 }887 }
868 switch (ty.zigTypeTag()) {888 switch (ty.zigTypeTag()) {
869 .Fn => {889 .Fn => {
870 const fn_decl = switch (val.tag()) {890 const fn_decl = switch (val.tag()) {
871 .extern_fn => val.castTag(.extern_fn).?.data,891 .extern_fn => val.castTag(.extern_fn).?.data,
872 .function => val.castTag(.function).?.data.owner_decl,892 .function => val.castTag(.function).?.data.owner_decl,
873 else => unreachable,893 else => unreachable,
874 };894 };
875 return try self.lowerDeclRef(ty, val, fn_decl);895 return try self.lowerDeclRef(ty, val, fn_decl, writer);
876 },896 },
877 .Optional => {897 .Optional => {
878 var opt_buf: Type.Payload.ElemType = undefined;898 var opt_buf: Type.Payload.ElemType = undefined;
879 const payload_type = ty.optionalChild(&opt_buf);899 const payload_type = ty.optionalChild(&opt_buf);
880 if (ty.isPtrLikeOptional()) {900 const is_pl = !val.isNull();
881 if (val.castTag(.opt_payload)) |payload| {901
882 return try self.genTypedValue(payload_type, payload.data);902 if (!payload_type.hasCodeGenBits()) {
883 } else if (!val.isNull()) {903 try writer.writeByteNTimes(@boolToInt(is_pl), 4);
884 return try self.genTypedValue(payload_type, val);
885 } else {
886 try self.code.appendNTimes(0, @intCast(usize, ty.abiSize(self.target)));
887 return Result.appended;904 return Result.appended;
888 }905 }
889 }906
890 // `null-tag` byte907 if (ty.isPtrLikeOptional()) {
891 try self.code.appendNTimes(@boolToInt(!val.isNull()), 4);908 if (val.castTag(.opt_payload)) |payload| {
892 const pl_result = try self.genTypedValue(909 return try self.genTypedValue(payload_type, payload.data, writer);
893 payload_type,910 } else if (!val.isNull()) {
894 if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),911 return try self.genTypedValue(payload_type, val, writer);
895 );912 } else {
896 switch (pl_result) {913 try writer.writeByteNTimes(0, @intCast(usize, ty.abiSize(self.target())));
897 .appended => {},914 return Result.appended;
898 .externally_managed => |payload| try self.code.appendSlice(payload),915 }
899 }916 }
900 return Result.appended;917
901 },918 // `null-tag` bytes
902 .Array => switch (val.tag()) {919 try writer.writeByteNTimes(@boolToInt(is_pl), 4);
903 .bytes => {920 const pl_result = try self.genTypedValue(
904 const payload = val.castTag(.bytes).?;921 payload_type,
905 return Result{ .externally_managed = payload.data };922 if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
923 writer,
924 );
925 switch (pl_result) {
926 .appended => {},
927 .externally_managed => |payload| try writer.writeAll(payload),
928 }
929 return Result.appended;
906 },930 },
907 .array => {931 .Array => switch (val.tag()) {
908 const elem_vals = val.castTag(.array).?.data;932 .bytes => {
909 const elem_ty = ty.childType();933 const payload = val.castTag(.bytes).?;
910 for (elem_vals) |elem_val| {934 return Result{ .externally_managed = payload.data };
911 switch (try self.genTypedValue(elem_ty, elem_val)) {935 },
936 .array => {
937 const elem_vals = val.castTag(.array).?.data;
938 const elem_ty = ty.childType();
939 for (elem_vals) |elem_val| {
940 switch (try self.genTypedValue(elem_ty, elem_val, writer)) {
941 .appended => {},
942 .externally_managed => |data| try writer.writeAll(data),
943 }
944 }
945 return Result.appended;
946 },
947 else => return self.fail("TODO implement genTypedValue for array type value: {s}", .{@tagName(val.tag())}),
948 },
949 .Int => {
950 const info = ty.intInfo(self.target());
951 const abi_size = @intCast(usize, ty.abiSize(self.target()));
952 // todo: Implement integer sizes larger than 64bits
953 if (info.bits > 64) return self.fail("TODO: Implement genTypedValue for integer bit size: {d}", .{info.bits});
954 var buf: [8]u8 = undefined;
955 if (info.signedness == .unsigned) {
956 std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt());
957 } else std.mem.writeIntLittle(i64, &buf, val.toSignedInt());
958 try writer.writeAll(buf[0..abi_size]);
959 return Result.appended;
960 },
961 .Enum => {
962 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
963 return Result.appended;
964 },
965 .Bool => {
966 try writer.writeByte(@boolToInt(val.toBool()));
967 return Result.appended;
968 },
969 .Struct => {
970 const field_vals = val.castTag(.@"struct").?.data;
971 for (field_vals) |field_val, index| {
972 const field_ty = ty.structFieldType(index);
973 if (!field_ty.hasCodeGenBits()) continue;
974 switch (try self.genTypedValue(field_ty, field_val, writer)) {
912 .appended => {},975 .appended => {},
913 .externally_managed => |data| try self.code.appendSlice(data),976 .externally_managed => |payload| try writer.writeAll(payload),
914 }977 }
915 }978 }
916 return Result.appended;979 return Result.appended;
917 },980 },
918 else => return self.fail("TODO implement genTypedValue for array type value: {s}", .{@tagName(val.tag())}),981 .Union => {
919 },982 // TODO: Implement Union declarations
920 .Int => {983 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
921 const info = ty.intInfo(self.target);984 return Result.appended;
922 const abi_size = @intCast(usize, ty.abiSize(self.target));
923 // todo: Implement integer sizes larger than 64bits
924 if (info.bits > 64) return self.fail("TODO: Implement genTypedValue for integer bit size: {d}", .{info.bits});
925 var buf: [8]u8 = undefined;
926 if (info.signedness == .unsigned) {
927 std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt());
928 } else std.mem.writeIntLittle(i64, &buf, val.toSignedInt());
929 try self.code.appendSlice(buf[0..abi_size]);
930 return Result.appended;
931 },
932 .Enum => {
933 const size = @intCast(usize, ty.abiSize(self.target));
934 try self.code.appendNTimes(0xaa, size);
935 return Result.appended;
936 },
937 .Bool => {
938 const int_byte: u8 = @boolToInt(val.toBool());
939 try self.code.append(int_byte);
940 return Result.appended;
941 },
942 .Struct => {
943 const field_vals = val.castTag(.@"struct").?.data;
944 for (field_vals) |field_val, index| {
945 const field_ty = ty.structFieldType(index);
946 if (!field_ty.hasCodeGenBits()) continue;
947 switch (try self.genTypedValue(field_ty, field_val)) {
948 .appended => {},
949 .externally_managed => |payload| try self.code.appendSlice(payload),
950 }
951 }
952 return Result.appended;
953 },
954 .Union => {
955 // TODO: Implement Union declarations
956 const abi_size = @intCast(usize, ty.abiSize(self.target));
957 try self.code.appendNTimes(0xaa, abi_size);
958 return Result.appended;
959 },
960 .Pointer => switch (val.tag()) {
961 .variable => {
962 const decl = val.castTag(.variable).?.data.owner_decl;
963 return try self.lowerDeclRef(ty, val, decl);
964 },985 },
965 .decl_ref => {986 .Pointer => switch (val.tag()) {
966 const decl = val.castTag(.decl_ref).?.data;987 .variable => {
967 return try self.lowerDeclRef(ty, val, decl);988 const decl = val.castTag(.variable).?.data.owner_decl;
989 return try self.lowerDeclRef(ty, val, decl, writer);
990 },
991 .decl_ref => {
992 const decl = val.castTag(.decl_ref).?.data;
993 return try self.lowerDeclRef(ty, val, decl, writer);
994 },
995 .slice => {
996 const slice = val.castTag(.slice).?.data;
997 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
998 const ptr_ty = ty.slicePtrFieldType(&buf);
999 switch (try self.genTypedValue(ptr_ty, slice.ptr, writer)) {
1000 .externally_managed => |data| try writer.writeAll(data),
1001 .appended => {},
1002 }
1003 switch (try self.genTypedValue(Type.usize, slice.len, writer)) {
1004 .externally_managed => |data| try writer.writeAll(data),
1005 .appended => {},
1006 }
1007 return Result.appended;
1008 },
1009 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),
968 },1010 },
969 .slice => {1011 .ErrorUnion => {
970 const slice = val.castTag(.slice).?.data;1012 const error_ty = ty.errorUnionSet();
971 var buf: Type.SlicePtrFieldTypeBuffer = undefined;1013 const payload_ty = ty.errorUnionPayload();
972 const ptr_ty = ty.slicePtrFieldType(&buf);1014 const is_pl = val.errorUnionIsPayload();
973 switch (try self.genTypedValue(ptr_ty, slice.ptr)) {1015
974 .externally_managed => |data| try self.code.appendSlice(data),1016 const err_val = if (!is_pl) val else Value.initTag(.zero);
1017 switch (try self.genTypedValue(error_ty, err_val, writer)) {
1018 .externally_managed => |data| try writer.writeAll(data),
975 .appended => {},1019 .appended => {},
976 }1020 }
977 switch (try self.genTypedValue(Type.usize, slice.len)) {1021
978 .externally_managed => |data| try self.code.appendSlice(data),1022 if (payload_ty.hasCodeGenBits()) {
979 .appended => {},1023 const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef);
1024 switch (try self.genTypedValue(payload_ty, pl_val, writer)) {
1025 .externally_managed => |data| try writer.writeAll(data),
1026 .appended => {},
1027 }
980 }1028 }
1029
981 return Result.appended;1030 return Result.appended;
982 },1031 },
983 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),1032 .ErrorSet => {
984 },1033 switch (val.tag()) {
985 .ErrorUnion => {1034 .@"error" => {
986 const error_ty = ty.errorUnionSet();1035 const name = val.castTag(.@"error").?.data.name;
987 const payload_ty = ty.errorUnionPayload();1036 const kv = try self.module.getErrorValue(name);
988 const is_pl = val.errorUnionIsPayload();1037 try writer.writeIntLittle(u32, kv.value);
9891038 },
990 const err_val = if (!is_pl) val else Value.initTag(.zero);1039 else => {
991 switch (try self.genTypedValue(error_ty, err_val)) {1040 try writer.writeByteNTimes(0, @intCast(usize, ty.abiSize(self.target())));
992 .externally_managed => |data| try self.code.appendSlice(data),1041 },
993 .appended => {},
994 }
995
996 if (payload_ty.hasCodeGenBits()) {
997 const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef);
998 switch (try self.genTypedValue(payload_ty, pl_val)) {
999 .externally_managed => |data| try self.code.appendSlice(data),
1000 .appended => {},
1001 }1042 }
1002 }1043 return Result.appended;
10031044 },
1004 return Result.appended;1045 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
1005 },1046 }
1006 .ErrorSet => {
1007 switch (val.tag()) {
1008 .@"error" => {
1009 const name = val.castTag(.@"error").?.data.name;
1010 const value = self.global_error_set.get(name).?;
1011 try self.code.writer().writeIntLittle(u32, value);
1012 },
1013 else => {
1014 const abi_size = @intCast(usize, ty.abiSize(self.target));
1015 try self.code.appendNTimes(0, abi_size);
1016 },
1017 }
1018 return Result.appended;
1019 },
1020 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
1021 }1047 }
1022}
10231048
1024fn lowerDeclRef(self: *Self, ty: Type, val: Value, decl: *Module.Decl) InnerError!Result {1049 fn lowerDeclRef(self: *DeclGen, ty: Type, val: Value, decl: *Module.Decl, writer: anytype) InnerError!Result {
1025 if (ty.isSlice()) {1050 if (ty.isSlice()) {
1026 var buf: Type.SlicePtrFieldTypeBuffer = undefined;1051 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1027 const slice_ty = ty.slicePtrFieldType(&buf);1052 const slice_ty = ty.slicePtrFieldType(&buf);
1028 switch (try self.genTypedValue(slice_ty, val)) {1053 switch (try self.genTypedValue(slice_ty, val, writer)) {
1029 .appended => {},1054 .appended => {},
1030 .externally_managed => |payload| try self.code.appendSlice(payload),1055 .externally_managed => |payload| try writer.writeAll(payload),
1056 }
1057 var slice_len: Value.Payload.U64 = .{
1058 .base = .{ .tag = .int_u64 },
1059 .data = val.sliceLen(),
1060 };
1061 return try self.genTypedValue(Type.usize, Value.initPayload(&slice_len.base), writer);
1031 }1062 }
1032 var slice_len: Value.Payload.U64 = .{
1033 .base = .{ .tag = .int_u64 },
1034 .data = val.sliceLen(),
1035 };
1036 return try self.genTypedValue(Type.usize, Value.initPayload(&slice_len.base));
1037 }
1038
1039 const offset = @intCast(u32, self.code.items.len);
1040 const atom = &self.decl.link.wasm;
1041 const target_sym_index = decl.link.wasm.sym_index;
1042 decl.markAlive();
1043 if (decl.ty.zigTypeTag() == .Fn) {
1044 // We found a function pointer, so add it to our table,
1045 // as function pointers are not allowed to be stored inside the data section,
1046 // but rather in a function table which are called by index
1047 try self.bin_file.addTableFunction(target_sym_index);
1048 try atom.relocs.append(self.gpa, .{
1049 .index = target_sym_index,
1050 .offset = offset,
1051 .relocation_type = .R_WASM_TABLE_INDEX_I32,
1052 });
1053 } else {
1054 try atom.relocs.append(self.gpa, .{
1055 .index = target_sym_index,
1056 .offset = offset,
1057 .relocation_type = .R_WASM_MEMORY_ADDR_I32,
1058 });
1059 }
1060 const ptr_width = @intCast(usize, self.target.cpu.arch.ptrBitWidth() / 8);
1061 try self.code.appendNTimes(0xaa, ptr_width);
10621063
1063 return Result.appended;1064 decl.markAlive();
1064}1065 try writer.writeIntLittle(u32, try self.bin_file.getDeclVAddr(
1066 self.decl, // The decl containing the source symbol index
1067 decl.ty, // type we generate the address of
1068 self.symbol_index, // source symbol index
1069 decl.link.wasm.sym_index, // target symbol index
1070 @intCast(u32, self.code.items.len), // offset
1071 ));
1072 return Result.appended;
1073 }
1074};
10651075
1066const CallWValues = struct {1076const CallWValues = struct {
1067 args: []WValue,1077 args: []WValue,
...@@ -1086,7 +1096,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu...@@ -1086,7 +1096,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
1086 const ret_ty = fn_ty.fnReturnType();1096 const ret_ty = fn_ty.fnReturnType();
1087 // Check if we store the result as a pointer to the stack rather than1097 // Check if we store the result as a pointer to the stack rather than
1088 // by value1098 // by value
1089 if (self.isByRef(ret_ty)) {1099 if (isByRef(ret_ty, self.target)) {
1090 // the sret arg will be passed as first argument, therefore we1100 // the sret arg will be passed as first argument, therefore we
1091 // set the `return_value` before allocating locals for regular args.1101 // set the `return_value` before allocating locals for regular args.
1092 result.return_value = .{ .local = self.local_index };1102 result.return_value = .{ .local = self.local_index };
...@@ -1213,8 +1223,8 @@ fn arch(self: *const Self) std.Target.Cpu.Arch {...@@ -1213,8 +1223,8 @@ fn arch(self: *const Self) std.Target.Cpu.Arch {
1213}1223}
12141224
1215/// For a given `Type`, will return true when the type will be passed1225/// For a given `Type`, will return true when the type will be passed
1216/// by reference, rather than by value.1226/// by reference, rather than by value
1217fn isByRef(self: Self, ty: Type) bool {1227fn isByRef(ty: Type, target: std.Target) bool {
1218 switch (ty.zigTypeTag()) {1228 switch (ty.zigTypeTag()) {
1219 .Type,1229 .Type,
1220 .ComptimeInt,1230 .ComptimeInt,
...@@ -1242,7 +1252,7 @@ fn isByRef(self: Self, ty: Type) bool {...@@ -1242,7 +1252,7 @@ fn isByRef(self: Self, ty: Type) bool {
1242 .Frame,1252 .Frame,
1243 .Union,1253 .Union,
1244 => return ty.hasCodeGenBits(),1254 => return ty.hasCodeGenBits(),
1245 .Int => return if (ty.intInfo(self.target).bits > 64) true else false,1255 .Int => return if (ty.intInfo(target).bits > 64) true else false,
1246 .ErrorUnion => {1256 .ErrorUnion => {
1247 const has_tag = ty.errorUnionSet().hasCodeGenBits();1257 const has_tag = ty.errorUnionSet().hasCodeGenBits();
1248 const has_pl = ty.errorUnionPayload().hasCodeGenBits();1258 const has_pl = ty.errorUnionPayload().hasCodeGenBits();
...@@ -1470,7 +1480,7 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1470,7 +1480,7 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1470 const child_type = self.air.typeOfIndex(inst).childType();1480 const child_type = self.air.typeOfIndex(inst).childType();
1471 if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} };1481 if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} };
14721482
1473 if (self.isByRef(child_type)) {1483 if (isByRef(child_type, self.target)) {
1474 return self.return_value;1484 return self.return_value;
1475 }1485 }
14761486
...@@ -1487,7 +1497,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1487,7 +1497,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1487 const ret_ty = self.air.typeOf(un_op).childType();1497 const ret_ty = self.air.typeOf(un_op).childType();
1488 if (!ret_ty.hasCodeGenBits()) return WValue.none;1498 if (!ret_ty.hasCodeGenBits()) return WValue.none;
14891499
1490 if (!self.isByRef(ret_ty)) {1500 if (!isByRef(ret_ty, self.target)) {
1491 const result = try self.load(operand, ret_ty, 0);1501 const result = try self.load(operand, ret_ty, 0);
1492 try self.emitWValue(result);1502 try self.emitWValue(result);
1493 }1503 }
...@@ -1509,7 +1519,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1509,7 +1519,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1509 else => unreachable,1519 else => unreachable,
1510 };1520 };
1511 const ret_ty = fn_ty.fnReturnType();1521 const ret_ty = fn_ty.fnReturnType();
1512 const first_param_sret = self.isByRef(ret_ty);1522 const first_param_sret = isByRef(ret_ty, self.target);
15131523
1514 const target: ?*Decl = blk: {1524 const target: ?*Decl = blk: {
1515 const func_val = self.air.value(pl_op.operand) orelse break :blk null;1525 const func_val = self.air.value(pl_op.operand) orelse break :blk null;
...@@ -1546,7 +1556,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1546,7 +1556,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1546 const operand = try self.resolveInst(pl_op.operand);1556 const operand = try self.resolveInst(pl_op.operand);
1547 try self.emitWValue(operand);1557 try self.emitWValue(operand);
15481558
1549 var fn_type = try self.genFunctype(fn_ty);1559 var fn_type = try genFunctype(self.gpa, fn_ty, self.target);
1550 defer fn_type.deinit(self.gpa);1560 defer fn_type.deinit(self.gpa);
15511561
1552 const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type);1562 const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type);
...@@ -1642,7 +1652,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1642,7 +1652,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1642 }1652 }
1643 try self.emitWValue(lhs);1653 try self.emitWValue(lhs);
1644 try self.emitWValue(rhs);1654 try self.emitWValue(rhs);
1645 const valtype = try self.typeToValtype(ty);1655 const valtype = typeToValtype(ty, self.target);
1646 // check if we should pass by pointer or value based on ABI size1656 // check if we should pass by pointer or value based on ABI size
1647 // TODO: Implement a way to get ABI values from a given type,1657 // TODO: Implement a way to get ABI values from a given type,
1648 // that is portable across the backend, rather than copying logic.1658 // that is portable across the backend, rather than copying logic.
...@@ -1675,7 +1685,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1675,7 +1685,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
16751685
1676 if (!ty.hasCodeGenBits()) return WValue{ .none = {} };1686 if (!ty.hasCodeGenBits()) return WValue{ .none = {} };
16771687
1678 if (self.isByRef(ty)) {1688 if (isByRef(ty, self.target)) {
1679 const new_local = try self.allocStack(ty);1689 const new_local = try self.allocStack(ty);
1680 try self.store(new_local, operand, ty, 0);1690 try self.store(new_local, operand, ty, 0);
1681 return new_local;1691 return new_local;
...@@ -1712,7 +1722,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {...@@ -1712,7 +1722,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
1712 };1722 };
17131723
1714 const opcode = buildOpcode(.{1724 const opcode = buildOpcode(.{
1715 .valtype1 = try self.typeToValtype(ty),1725 .valtype1 = typeToValtype(ty, self.target),
1716 .width = abi_size * 8, // use bitsize instead of byte size1726 .width = abi_size * 8, // use bitsize instead of byte size
1717 .op = .load,1727 .op = .load,
1718 .signedness = signedness,1728 .signedness = signedness,
...@@ -1743,7 +1753,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -1743,7 +1753,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1743 const rhs = try self.resolveInst(bin_op.rhs);1753 const rhs = try self.resolveInst(bin_op.rhs);
1744 const operand_ty = self.air.typeOfIndex(inst);1754 const operand_ty = self.air.typeOfIndex(inst);
17451755
1746 if (self.isByRef(operand_ty)) {1756 if (isByRef(operand_ty, self.target)) {
1747 return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty});1757 return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty});
1748 }1758 }
17491759
...@@ -1753,7 +1763,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -1753,7 +1763,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1753 const bin_ty = self.air.typeOf(bin_op.lhs);1763 const bin_ty = self.air.typeOf(bin_op.lhs);
1754 const opcode: wasm.Opcode = buildOpcode(.{1764 const opcode: wasm.Opcode = buildOpcode(.{
1755 .op = op,1765 .op = op,
1756 .valtype1 = try self.typeToValtype(bin_ty),1766 .valtype1 = typeToValtype(bin_ty, self.target),
1757 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,1767 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
1758 });1768 });
1759 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));1769 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
...@@ -1775,7 +1785,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -1775,7 +1785,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1775 const bin_ty = self.air.typeOf(bin_op.lhs);1785 const bin_ty = self.air.typeOf(bin_op.lhs);
1776 const opcode: wasm.Opcode = buildOpcode(.{1786 const opcode: wasm.Opcode = buildOpcode(.{
1777 .op = op,1787 .op = op,
1778 .valtype1 = try self.typeToValtype(bin_ty),1788 .valtype1 = typeToValtype(bin_ty, self.target),
1779 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,1789 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
1780 });1790 });
1781 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));1791 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
...@@ -1924,8 +1934,8 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {...@@ -1924,8 +1934,8 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
1924 },1934 },
1925 .ErrorSet => switch (val.tag()) {1935 .ErrorSet => switch (val.tag()) {
1926 .@"error" => {1936 .@"error" => {
1927 const error_index = self.global_error_set.get(val.getError().?).?;1937 const kv = try self.module.getErrorValue(val.getError().?);
1928 return WValue{ .imm32 = error_index };1938 return WValue{ .imm32 = kv.value };
1929 },1939 },
1930 else => return WValue{ .imm32 = 0 },1940 else => return WValue{ .imm32 = 0 },
1931 },1941 },
...@@ -1945,7 +1955,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {...@@ -1945,7 +1955,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
1945 if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef),1955 if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef),
1946 payload_type,1956 payload_type,
1947 );1957 );
1948 const pl_ptr = if (self.isByRef(payload_type))1958 const pl_ptr = if (isByRef(payload_type, self.target))
1949 try self.buildPointerOffset(result, error_type.abiSize(self.target), .new)1959 try self.buildPointerOffset(result, error_type.abiSize(self.target), .new)
1950 else1960 else
1951 result;1961 result;
...@@ -2159,8 +2169,8 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {...@@ -2159,8 +2169,8 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
2159 .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt())),2169 .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt())),
2160 },2170 },
2161 .ErrorSet => {2171 .ErrorSet => {
2162 const error_index = self.global_error_set.get(val.getError().?).?;2172 const kv = self.module.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function
2163 return @bitCast(i32, error_index);2173 return @bitCast(i32, kv.value);
2164 },2174 },
2165 else => unreachable, // Programmer called this function for an illegal type2175 else => unreachable, // Programmer called this function for an illegal type
2166 }2176 }
...@@ -2168,7 +2178,7 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {...@@ -2168,7 +2178,7 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
21682178
2169fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2179fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2170 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2180 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2171 const block_ty = try self.genBlockType(self.air.getRefType(ty_pl.ty));2181 const block_ty = genBlockType(self.air.getRefType(ty_pl.ty), self.target);
2172 const extra = self.air.extraData(Air.Block, ty_pl.payload);2182 const extra = self.air.extraData(Air.Block, ty_pl.payload);
2173 const body = self.air.extra[extra.end..][0..extra.data.body_len];2183 const body = self.air.extra[extra.end..][0..extra.data.body_len];
21742184
...@@ -2265,7 +2275,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner...@@ -2265,7 +2275,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
2265 // both lhs and rhs, as well as checking the payload are matching of lhs and rhs2275 // both lhs and rhs, as well as checking the payload are matching of lhs and rhs
2266 return self.cmpOptionals(lhs, rhs, operand_ty, op);2276 return self.cmpOptionals(lhs, rhs, operand_ty, op);
2267 }2277 }
2268 } else if (self.isByRef(operand_ty)) {2278 } else if (isByRef(operand_ty, self.target)) {
2269 return self.cmpBigInt(lhs, rhs, operand_ty, op);2279 return self.cmpBigInt(lhs, rhs, operand_ty, op);
2270 }2280 }
22712281
...@@ -2280,7 +2290,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner...@@ -2280,7 +2290,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
2280 break :blk operand_ty.intInfo(self.target).signedness;2290 break :blk operand_ty.intInfo(self.target).signedness;
2281 };2291 };
2282 const opcode: wasm.Opcode = buildOpcode(.{2292 const opcode: wasm.Opcode = buildOpcode(.{
2283 .valtype1 = try self.typeToValtype(operand_ty),2293 .valtype1 = typeToValtype(operand_ty, self.target),
2284 .op = switch (op) {2294 .op = switch (op) {
2285 .lt => .lt,2295 .lt => .lt,
2286 .lte => .le,2296 .lte => .le,
...@@ -2353,13 +2363,6 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2353,13 +2363,6 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2353fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2363fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2354 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2364 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2355 const operand = try self.resolveInst(ty_op.operand);2365 const operand = try self.resolveInst(ty_op.operand);
2356 // if (operand == .constant) {
2357 // std.debug.print("Let's take a look at this!!!!!!\n--------------------\n", .{});
2358 // const result = try self.allocLocal(self.air.typeOfIndex(inst));
2359 // try self.emitWValue(operand);
2360 // try self.addLabel(.local_set, result.local);
2361 // return result;
2362 // }
2363 return operand;2366 return operand;
2364}2367}
23652368
...@@ -2407,7 +2410,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2407,7 +2410,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2407 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});2410 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
2408 };2411 };
24092412
2410 if (self.isByRef(field_ty)) {2413 if (isByRef(field_ty, self.target)) {
2411 return self.buildPointerOffset(operand, offset, .new);2414 return self.buildPointerOffset(operand, offset, .new);
2412 }2415 }
24132416
...@@ -2527,7 +2530,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2527,7 +2530,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2527 const val = try self.lowerConstant(case.values[0].value, target_ty);2530 const val = try self.lowerConstant(case.values[0].value, target_ty);
2528 try self.emitWValue(val);2531 try self.emitWValue(val);
2529 const opcode = buildOpcode(.{2532 const opcode = buildOpcode(.{
2530 .valtype1 = try self.typeToValtype(target_ty),2533 .valtype1 = typeToValtype(target_ty, self.target),
2531 .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition.2534 .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition.
2532 .signedness = signedness,2535 .signedness = signedness,
2533 });2536 });
...@@ -2541,7 +2544,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2541,7 +2544,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2541 const val = try self.lowerConstant(value.value, target_ty);2544 const val = try self.lowerConstant(value.value, target_ty);
2542 try self.emitWValue(val);2545 try self.emitWValue(val);
2543 const opcode = buildOpcode(.{2546 const opcode = buildOpcode(.{
2544 .valtype1 = try self.typeToValtype(target_ty),2547 .valtype1 = typeToValtype(target_ty, self.target),
2545 .op = .eq,2548 .op = .eq,
2546 .signedness = signedness,2549 .signedness = signedness,
2547 });2550 });
...@@ -2596,7 +2599,7 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue...@@ -2596,7 +2599,7 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue
2596 const payload_ty = err_ty.errorUnionPayload();2599 const payload_ty = err_ty.errorUnionPayload();
2597 if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} };2600 if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} };
2598 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target));2601 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target));
2599 if (self.isByRef(payload_ty)) {2602 if (isByRef(payload_ty, self.target)) {
2600 return self.buildPointerOffset(operand, offset, .new);2603 return self.buildPointerOffset(operand, offset, .new);
2601 }2604 }
2602 return try self.load(operand, payload_ty, offset);2605 return try self.load(operand, payload_ty, offset);
...@@ -2725,7 +2728,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2725,7 +2728,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27252728
2726 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);2729 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);
27272730
2728 if (self.isByRef(payload_ty)) {2731 if (isByRef(payload_ty, self.target)) {
2729 return self.buildPointerOffset(operand, offset, .new);2732 return self.buildPointerOffset(operand, offset, .new);
2730 }2733 }
27312734
...@@ -2856,7 +2859,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2856,7 +2859,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2856 const result = try self.allocLocal(elem_ty);2859 const result = try self.allocLocal(elem_ty);
2857 try self.addLabel(.local_set, result.local);2860 try self.addLabel(.local_set, result.local);
28582861
2859 if (self.isByRef(elem_ty)) {2862 if (isByRef(elem_ty, self.target)) {
2860 return result;2863 return result;
2861 }2864 }
2862 return try self.load(result, elem_ty, 0);2865 return try self.load(result, elem_ty, 0);
...@@ -3017,7 +3020,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3017,7 +3020,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30173020
3018 const result = try self.allocLocal(elem_ty);3021 const result = try self.allocLocal(elem_ty);
3019 try self.addLabel(.local_set, result.local);3022 try self.addLabel(.local_set, result.local);
3020 if (self.isByRef(elem_ty)) {3023 if (isByRef(elem_ty, self.target)) {
3021 return result;3024 return result;
3022 }3025 }
3023 return try self.load(result, elem_ty, 0);3026 return try self.load(result, elem_ty, 0);
...@@ -3064,7 +3067,7 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -3064,7 +3067,7 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
3064 else => ptr_ty.childType(),3067 else => ptr_ty.childType(),
3065 };3068 };
30663069
3067 const valtype = try self.typeToValtype(Type.usize);3070 const valtype = typeToValtype(Type.usize, self.target);
3068 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });3071 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });
3069 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });3072 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });
30703073
...@@ -3167,7 +3170,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3167,7 +3170,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3167 const result = try self.allocLocal(elem_ty);3170 const result = try self.allocLocal(elem_ty);
3168 try self.addLabel(.local_set, result.local);3171 try self.addLabel(.local_set, result.local);
31693172
3170 if (self.isByRef(elem_ty)) {3173 if (isByRef(elem_ty, self.target)) {
3171 return result;3174 return result;
3172 }3175 }
3173 return try self.load(result, elem_ty, 0);3176 return try self.load(result, elem_ty, 0);
...@@ -3184,8 +3187,8 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3184,8 +3187,8 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3184 try self.emitWValue(operand);3187 try self.emitWValue(operand);
3185 const op = buildOpcode(.{3188 const op = buildOpcode(.{
3186 .op = .trunc,3189 .op = .trunc,
3187 .valtype1 = try self.typeToValtype(dest_ty),3190 .valtype1 = typeToValtype(dest_ty, self.target),
3188 .valtype2 = try self.typeToValtype(op_ty),3191 .valtype2 = typeToValtype(op_ty, self.target),
3189 .signedness = if (dest_ty.isSignedInt()) .signed else .unsigned,3192 .signedness = if (dest_ty.isSignedInt()) .signed else .unsigned,
3190 });3193 });
3191 try self.addTag(Mir.Inst.Tag.fromOpcode(op));3194 try self.addTag(Mir.Inst.Tag.fromOpcode(op));
...@@ -3249,7 +3252,7 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std...@@ -3249,7 +3252,7 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
32493252
3250 try self.emitWValue(lhs_pl);3253 try self.emitWValue(lhs_pl);
3251 try self.emitWValue(rhs_pl);3254 try self.emitWValue(rhs_pl);
3252 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = try self.typeToValtype(payload_ty) });3255 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, self.target) });
3253 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));3256 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3254 try self.addLabel(.br_if, 0);3257 try self.addLabel(.br_if, 0);
32553258
src/link/Wasm.zig+55-25
...@@ -234,12 +234,12 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live...@@ -234,12 +234,12 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
234 .locals = .{},234 .locals = .{},
235 .target = self.base.options.target,235 .target = self.base.options.target,
236 .bin_file = self,236 .bin_file = self,
237 .global_error_set = self.base.options.module.?.global_error_set,237 .module = module,
238 };238 };
239 defer codegen.deinit();239 defer codegen.deinit();
240240
241 // generate the 'code' section for the function declaration241 // generate the 'code' section for the function declaration
242 const result = codegen.genFunc() catch |err| switch (err) {242 codegen.genFunc() catch |err| switch (err) {
243 error.CodegenFail => {243 error.CodegenFail => {
244 decl.analysis = .codegen_failure;244 decl.analysis = .codegen_failure;
245 try module.failed_decls.put(module.gpa, decl, codegen.err_msg);245 try module.failed_decls.put(module.gpa, decl, codegen.err_msg);
...@@ -247,7 +247,7 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live...@@ -247,7 +247,7 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
247 },247 },
248 else => |e| return e,248 else => |e| return e,
249 };249 };
250 return self.finishUpdateDecl(decl, result, &codegen);250 return self.finishUpdateDecl(decl, codegen.code.items);
251}251}
252252
253// Generate code for the Decl, storing it in memory to be later written to253// Generate code for the Decl, storing it in memory to be later written to
...@@ -264,40 +264,37 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {...@@ -264,40 +264,37 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
264264
265 decl.link.wasm.clear();265 decl.link.wasm.clear();
266266
267 var codegen: CodeGen = .{267 var code_writer = std.ArrayList(u8).init(self.base.allocator);
268 defer code_writer.deinit();
269 var decl_gen: CodeGen.DeclGen = .{
268 .gpa = self.base.allocator,270 .gpa = self.base.allocator,
269 .air = undefined,
270 .liveness = undefined,
271 .values = .{},
272 .code = std.ArrayList(u8).init(self.base.allocator),
273 .decl = decl,271 .decl = decl,
274 .err_msg = undefined,272 .symbol_index = decl.link.wasm.sym_index,
275 .locals = .{},
276 .target = self.base.options.target,
277 .bin_file = self,273 .bin_file = self,
278 .global_error_set = self.base.options.module.?.global_error_set,274 .err_msg = undefined,
275 .code = &code_writer,
276 .module = module,
279 };277 };
280 defer codegen.deinit();
281278
282 // generate the 'code' section for the function declaration279 // generate the 'code' section for the function declaration
283 const result = codegen.genDecl() catch |err| switch (err) {280 const result = decl_gen.genDecl() catch |err| switch (err) {
284 error.CodegenFail => {281 error.CodegenFail => {
285 decl.analysis = .codegen_failure;282 decl.analysis = .codegen_failure;
286 try module.failed_decls.put(module.gpa, decl, codegen.err_msg);283 try module.failed_decls.put(module.gpa, decl, decl_gen.err_msg);
287 return;284 return;
288 },285 },
289 else => |e| return e,286 else => |e| return e,
290 };287 };
291288
292 return self.finishUpdateDecl(decl, result, &codegen);289 const code = switch (result) {
293}290 .externally_managed => |data| data,
294291 .appended => code_writer.items,
295fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, result: CodeGen.Result, codegen: *CodeGen) !void {
296 const code: []const u8 = switch (result) {
297 .appended => @as([]const u8, codegen.code.items),
298 .externally_managed => |payload| payload,
299 };292 };
300293
294 return self.finishUpdateDecl(decl, code);
295}
296
297fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
301 if (decl.isExtern()) {298 if (decl.isExtern()) {
302 try self.addOrUpdateImport(decl);299 try self.addOrUpdateImport(decl);
303 return;300 return;
...@@ -313,7 +310,7 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, result: CodeGen.Result, cod...@@ -313,7 +310,7 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, result: CodeGen.Result, cod
313310
314/// Creates a new local symbol for a given type (and its bytes it's represented by)311/// Creates a new local symbol for a given type (and its bytes it's represented by)
315/// and then append it as a 'contained' atom onto the Decl.312/// and then append it as a 'contained' atom onto the Decl.
316pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type, code: []const u8) !u32 {313pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type) !u32 {
317 assert(ty.zigTypeTag() != .Fn); // cannot create local symbols for functions314 assert(ty.zigTypeTag() != .Fn); // cannot create local symbols for functions
318 var symbol: Symbol = .{315 var symbol: Symbol = .{
319 .name = "unnamed_local",316 .name = "unnamed_local",
...@@ -325,9 +322,7 @@ pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type, code: []cons...@@ -325,9 +322,7 @@ pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type, code: []cons
325 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);322 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
326323
327 var atom = Atom.empty;324 var atom = Atom.empty;
328 atom.size = @intCast(u32, code.len);
329 atom.alignment = ty.abiAlignment(self.base.options.target);325 atom.alignment = ty.abiAlignment(self.base.options.target);
330 try atom.code.appendSlice(self.base.allocator, code);
331326
332 if (self.symbols_free_list.popOrNull()) |index| {327 if (self.symbols_free_list.popOrNull()) |index| {
333 atom.sym_index = index;328 atom.sym_index = index;
...@@ -341,6 +336,41 @@ pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type, code: []cons...@@ -341,6 +336,41 @@ pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type, code: []cons
341 return atom.sym_index;336 return atom.sym_index;
342}337}
343338
339pub fn updateLocalSymbolCode(self: *Wasm, decl: *Module.Decl, symbol_index: u32, code: []const u8) !void {
340 const atom = decl.link.wasm.symbolAtom(symbol_index);
341 atom.size = @intCast(u32, code.len);
342 try atom.code.appendSlice(self.base.allocator, code);
343}
344
345/// For a given decl, find the given symbol index's atom, and create a relocation for the type.
346/// Returns the given pointer address
347pub fn getDeclVAddr(self: *Wasm, decl: *Module.Decl, ty: Type, symbol_index: u32, target_symbol_index: u32, offset: u32) !u32 {
348 const atom = decl.link.wasm.symbolAtom(symbol_index);
349 const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;
350 if (ty.zigTypeTag() == .Fn) {
351 // We found a function pointer, so add it to our table,
352 // as function pointers are not allowed to be stored inside the data section.
353 // They are instead stored in a function table which are called by index.
354 try self.addTableFunction(target_symbol_index);
355 try atom.relocs.append(self.base.allocator, .{
356 .index = target_symbol_index,
357 .offset = offset,
358 .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
359 });
360 } else {
361 try atom.relocs.append(self.base.allocator, .{
362 .index = target_symbol_index,
363 .offset = offset,
364 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
365 });
366 }
367 // we do not know the final address at this point,
368 // as atom allocation will determine the address and relocations
369 // will calculate and rewrite this. Therefore, we simply return the symbol index
370 // that was targeted.
371 return target_symbol_index;
372}
373
344pub fn updateDeclExports(374pub fn updateDeclExports(
345 self: *Wasm,375 self: *Wasm,
346 module: *Module,376 module: *Module,
src/link/Wasm/Atom.zig+18
...@@ -31,6 +31,10 @@ next: ?*Atom,...@@ -31,6 +31,10 @@ next: ?*Atom,
31/// is null when this atom is the first in its order31/// is null when this atom is the first in its order
32prev: ?*Atom,32prev: ?*Atom,
3333
34/// Contains atoms local to a decl, all managed by this `Atom`.
35/// When the parent atom is being freed, it will also do so for all local atoms.
36locals: std.ArrayListUnmanaged(Atom) = .{},
37
34/// Represents a default empty wasm `Atom`38/// Represents a default empty wasm `Atom`
35pub const empty: Atom = .{39pub const empty: Atom = .{
36 .alignment = 0,40 .alignment = 0,
...@@ -45,6 +49,11 @@ pub const empty: Atom = .{...@@ -45,6 +49,11 @@ pub const empty: Atom = .{
45pub fn deinit(self: *Atom, gpa: Allocator) void {49pub fn deinit(self: *Atom, gpa: Allocator) void {
46 self.relocs.deinit(gpa);50 self.relocs.deinit(gpa);
47 self.code.deinit(gpa);51 self.code.deinit(gpa);
52
53 while (self.locals.popOrNull()) |*local| {
54 local.deinit(gpa);
55 }
56 self.locals.deinit(gpa);
48}57}
4958
50/// Sets the length of relocations and code to '0',59/// Sets the length of relocations and code to '0',
...@@ -72,6 +81,15 @@ pub fn getFirst(self: *Atom) *Atom {...@@ -72,6 +81,15 @@ pub fn getFirst(self: *Atom) *Atom {
72 return tmp;81 return tmp;
73}82}
7483
84/// Returns the atom for the given `symbol_index`.
85/// This can be either the `Atom` itself, or one of its locals.
86pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {
87 if (self.sym_index == symbol_index) return self;
88 return for (self.locals.items) |*local_atom| {
89 if (local_atom.sym_index == symbol_index) break local_atom;
90 } else unreachable; // Used a symbol index not present in this atom or its children.
91}
92
75/// Resolves the relocations within the atom, writing the new value93/// Resolves the relocations within the atom, writing the new value
76/// at the calculated offset.94/// at the calculated offset.
77pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {95pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {