authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-20 20:26:09+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-20 20:26:09+01:00
logeb70f6e8d7f4c1a735fe25de368f6d5459cba16c
treee81e0f8798332aa7ae6580e3931bf29e400d1786
parent664e1a892c3b3a1162fa5b8eaa3762ad581b1d1b
parentb9fe6a93ff51ecb5ce770c78f463c38a0620de49
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10638 from Luukdegram/wasm-refactor

Stage2: wasm - Refactor lowering constants

6 files changed, 716 insertions(+), 815 deletions(-)

src/arch/wasm/CodeGen.zig+573-762
......@@ -27,18 +27,22 @@ const WValue = union(enum) {
2727 none: void,
2828 /// Index of the local variable
2929 local: u32,
30 /// Holds a memoized typed value
31 constant: TypedValue,
32 /// Used for types that contains of multiple areas within
33 /// a memory region in the stack.
34 /// The local represents the position in the stack,
35 /// whereas the offset represents the offset from that position.
36 local_with_offset: struct {
37 /// Index of the local variable
38 local: u32,
39 /// The offset from the local's stack position
40 offset: u32,
41 },
30 /// An immediate 32bit value
31 imm32: u32,
32 /// An immediate 64bit value
33 imm64: u64,
34 /// A constant 32bit float value
35 float32: f32,
36 /// A constant 64bit float value
37 float64: f64,
38 /// A value that represents a pointer to the data section
39 /// Note: The value contains the symbol index, rather than the actual address
40 /// as we use this to perform the relocation.
41 memory: u32,
42 /// Represents a function pointer
43 /// In wasm function pointers are indexes into a function table,
44 /// rather than an address in the data section.
45 function_index: u32,
4246};
4347
4448/// Wasm ops, but without input/output/signedness information
......@@ -500,7 +504,7 @@ pub const Result = union(enum) {
500504};
501505
502506/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
503pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Index, WValue);
507pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Ref, WValue);
504508
505509const Self = @This();
506510
......@@ -538,10 +542,9 @@ locals: std.ArrayListUnmanaged(u8),
538542target: std.Target,
539543/// Represents the wasm binary file that is being linked.
540544bin_file: *link.File.Wasm,
541/// Table with the global error set. Consists of every error found in
542/// the compiled code. Each error name maps to a `Module.ErrorInt` which is emitted
543/// during codegen to determine the error value.
544global_error_set: std.StringHashMapUnmanaged(Module.ErrorInt),
545/// Reference to the Module that this decl is part of.
546/// Used to find the error value.
547module: *Module,
545548/// List of MIR Instructions
546549mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
547550/// Contains extra data for MIR
......@@ -577,7 +580,7 @@ pub fn deinit(self: *Self) void {
577580 self.* = undefined;
578581}
579582
580/// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig
583/// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig
581584fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError {
582585 const src: LazySrcLoc = .{ .node_offset = 0 };
583586 const src_loc = src.toSrcLoc(self.decl);
......@@ -587,25 +590,52 @@ fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError {
587590
588591/// Resolves the `WValue` for the given instruction `inst`
589592/// When the given instruction has a `Value`, it returns a constant instead
590fn resolveInst(self: Self, ref: Air.Inst.Ref) WValue {
591 const inst_index = Air.refToIndex(ref) orelse {
592 const tv = Air.Inst.Ref.typed_value_map[@enumToInt(ref)];
593 if (!tv.ty.hasCodeGenBits()) {
594 return WValue.none;
595 }
596 return WValue{ .constant = tv };
597 };
598
599 const inst_type = self.air.typeOfIndex(inst_index);
600 // It's allowed to have 0-bit integers
601 if (!inst_type.hasCodeGenBits() and !inst_type.isInt()) return WValue{ .none = {} };
602
603 if (self.air.instructions.items(.tag)[inst_index] == .constant) {
604 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
605 return WValue{ .constant = .{ .ty = inst_type, .val = self.air.values[ty_pl.payload] } };
606 }
593fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
594 const gop = try self.values.getOrPut(self.gpa, ref);
595 if (gop.found_existing) return gop.value_ptr.*;
596
597 // when we did not find an existing instruction, it
598 // means we must generate it from a constant.
599 const val = self.air.value(ref).?;
600 const ty = self.air.typeOf(ref);
601 if (!ty.hasCodeGenBits() and !ty.isInt()) return WValue{ .none = {} };
602
603 // When we need to pass the value by reference (such as a struct), we will
604 // leverage `genTypedValue` to lower the constant to bytes and emit it
605 // to the 'rodata' section. We then return the index into the section as `WValue`.
606 //
607 // In the other cases, we will simply lower the constant to a value that fits
608 // into a single local (such as a pointer, integer, bool, etc).
609 const result = if (isByRef(ty, self.target)) blk: {
610 var value_bytes = std.ArrayList(u8).init(self.gpa);
611 defer value_bytes.deinit();
612
613 var decl_gen: DeclGen = .{
614 .bin_file = self.bin_file,
615 .decl = self.decl,
616 .err_msg = undefined,
617 .gpa = self.gpa,
618 .module = self.module,
619 .code = &value_bytes,
620 .symbol_index = try self.bin_file.createLocalSymbol(self.decl, ty),
621 };
622 const result = decl_gen.genTypedValue(ty, val, value_bytes.writer()) catch |err| {
623 // When a codegen error occured, take ownership of the error message
624 if (err == error.CodegenFail) {
625 self.err_msg = decl_gen.err_msg;
626 }
627 return err;
628 };
629 const code = switch (result) {
630 .appended => value_bytes.items,
631 .externally_managed => |data| data,
632 };
633 try self.bin_file.updateLocalSymbolCode(self.decl, decl_gen.symbol_index, code);
634 break :blk WValue{ .memory = decl_gen.symbol_index };
635 } else try self.lowerConstant(val, ty);
607636
608 return self.values.get(inst_index).?; // Instruction does not dominate all uses!
637 gop.value_ptr.* = result;
638 return result;
609639}
610640
611641/// Appends a MIR instruction and returns its index within the list of instructions
......@@ -677,60 +707,55 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) error{OutOfMemory}!u32 {
677707 return result;
678708}
679709
680/// Using a given `Type`, returns the corresponding wasm Valtype
681fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {
710/// Using a given `Type`, returns the corresponding type
711fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {
682712 return switch (ty.zigTypeTag()) {
683713 .Float => blk: {
684 const bits = ty.floatBits(self.target);
714 const bits = ty.floatBits(target);
685715 if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32;
686716 if (bits == 64) break :blk wasm.Valtype.f64;
687 return self.fail("Float bit size not supported by wasm: '{d}'", .{bits});
717 return wasm.Valtype.i32; // represented as pointer to stack
688718 },
689719 .Int => blk: {
690 const info = ty.intInfo(self.target);
720 const info = ty.intInfo(target);
691721 if (info.bits <= 32) break :blk wasm.Valtype.i32;
692722 if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64;
693723 break :blk wasm.Valtype.i32; // represented as pointer to stack
694724 },
695725 .Enum => switch (ty.tag()) {
696726 .enum_simple => wasm.Valtype.i32,
697 else => self.typeToValtype(ty.cast(Type.Payload.EnumFull).?.data.tag_ty),
727 else => typeToValtype(ty.cast(Type.Payload.EnumFull).?.data.tag_ty, target),
698728 },
699 .Bool,
700 .Pointer,
701 .ErrorSet,
702 .Struct,
703 .ErrorUnion,
704 .Optional,
705 .Fn,
706 .Array,
707 => wasm.Valtype.i32,
708 else => self.fail("TODO - Wasm typeToValtype for type '{}'", .{ty}),
729 else => wasm.Valtype.i32, // all represented as reference/immediate
709730 };
710731}
711732
712733/// Using a given `Type`, returns the byte representation of its wasm value type
713fn genValtype(self: *Self, ty: Type) InnerError!u8 {
714 return wasm.valtype(try self.typeToValtype(ty));
734fn genValtype(ty: Type, target: std.Target) u8 {
735 return wasm.valtype(typeToValtype(ty, target));
715736}
716737
717738/// Using a given `Type`, returns the corresponding wasm value type
718739/// Differently from `genValtype` this also allows `void` to create a block
719740/// with no return type
720fn genBlockType(self: *Self, ty: Type) InnerError!u8 {
741fn genBlockType(ty: Type, target: std.Target) u8 {
721742 return switch (ty.tag()) {
722743 .void, .noreturn => wasm.block_empty,
723 else => self.genValtype(ty),
744 else => genValtype(ty, target),
724745 };
725746}
726747
727748/// Writes the bytecode depending on the given `WValue` in `val`
728fn emitWValue(self: *Self, val: WValue) InnerError!void {
729 switch (val) {
749fn emitWValue(self: *Self, value: WValue) InnerError!void {
750 switch (value) {
730751 .none => {}, // no-op
731 .local_with_offset => |with_off| try self.addLabel(.local_get, with_off.local),
732752 .local => |idx| try self.addLabel(.local_get, idx),
733 .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack
753 .imm32 => |val| try self.addImm32(@bitCast(i32, val)),
754 .imm64 => |val| try self.addImm64(val),
755 .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
756 .float64 => |val| try self.addFloat64(val),
757 .memory => |ptr| try self.addLabel(.memory_address, ptr), // write sybol address and generate relocation
758 .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation
734759 }
735760}
736761
......@@ -738,7 +763,7 @@ fn emitWValue(self: *Self, val: WValue) InnerError!void {
738763/// Returns a corresponding `Wvalue` with `local` as active tag
739764fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
740765 const initial_index = self.local_index;
741 const valtype = try self.genValtype(ty);
766 const valtype = genValtype(ty, self.target);
742767 try self.locals.append(self.gpa, valtype);
743768 self.local_index += 1;
744769 return WValue{ .local = initial_index };
......@@ -746,33 +771,33 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
746771
747772/// Generates a `wasm.Type` from a given function type.
748773/// Memory is owned by the caller.
749fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {
750 var params = std.ArrayList(wasm.Valtype).init(self.gpa);
774fn genFunctype(gpa: Allocator, fn_ty: Type, target: std.Target) !wasm.Type {
775 var params = std.ArrayList(wasm.Valtype).init(gpa);
751776 defer params.deinit();
752 var returns = std.ArrayList(wasm.Valtype).init(self.gpa);
777 var returns = std.ArrayList(wasm.Valtype).init(gpa);
753778 defer returns.deinit();
754779 const return_type = fn_ty.fnReturnType();
755780
756 const want_sret = self.isByRef(return_type);
781 const want_sret = isByRef(return_type, target);
757782
758783 if (want_sret) {
759 try params.append(try self.typeToValtype(Type.usize));
784 try params.append(typeToValtype(return_type, target));
760785 }
761786
762787 // param types
763788 if (fn_ty.fnParamLen() != 0) {
764 const fn_params = try self.gpa.alloc(Type, fn_ty.fnParamLen());
765 defer self.gpa.free(fn_params);
789 const fn_params = try gpa.alloc(Type, fn_ty.fnParamLen());
790 defer gpa.free(fn_params);
766791 fn_ty.fnParamTypes(fn_params);
767792 for (fn_params) |param_type| {
768793 if (!param_type.hasCodeGenBits()) continue;
769 try params.append(try self.typeToValtype(param_type));
794 try params.append(typeToValtype(param_type, target));
770795 }
771796 }
772797
773798 // return type
774799 if (!want_sret and return_type.hasCodeGenBits()) {
775 try returns.append(try self.typeToValtype(return_type));
800 try returns.append(typeToValtype(return_type, target));
776801 }
777802
778803 return wasm.Type{
......@@ -781,8 +806,8 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {
781806 };
782807}
783808
784pub fn genFunc(self: *Self) InnerError!Result {
785 var func_type = try self.genFunctype(self.decl.ty);
809pub fn genFunc(self: *Self) InnerError!void {
810 var func_type = try genFunctype(self.gpa, self.decl.ty, self.target);
786811 defer func_type.deinit(self.gpa);
787812 self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
788813
......@@ -827,239 +852,300 @@ pub fn genFunc(self: *Self) InnerError!Result {
827852 },
828853 else => |e| return e,
829854 };
830
831 // codegen data has been appended to `code`
832 return Result.appended;
833855}
834856
835pub fn genDecl(self: *Self) InnerError!Result {
836 const decl = self.decl;
837 assert(decl.has_tv);
838
839 log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val });
840
841 if (decl.val.castTag(.function)) |func_payload| {
842 _ = func_payload;
843 return self.fail("TODO wasm backend genDecl function pointer", .{});
844 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {
845 const ext_decl = extern_fn.data;
846 var func_type = try self.genFunctype(ext_decl.ty);
847 func_type.deinit(self.gpa);
848 ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
849 return Result.appended;
850 } else {
851 const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
852 break :init_val payload.data.init;
853 } else decl.val;
854 if (init_val.tag() != .unreachable_value) {
855 return try self.genTypedValue(decl.ty, init_val);
857pub const DeclGen = struct {
858 /// The decl we are generating code for.
859 decl: *Decl,
860 /// The symbol we're generating code for.
861 /// This can either be the symbol of the Decl itself,
862 /// or one of its locals.
863 symbol_index: u32,
864 gpa: Allocator,
865 /// A reference to the linker, that will process the decl's
866 /// code and create any relocations it deems neccesary.
867 bin_file: *link.File.Wasm,
868 /// This will be set when `InnerError` has been returned.
869 /// In any other case, this will be 'undefined'.
870 err_msg: *Module.ErrorMsg,
871 /// Reference to the Module that is being compiled.
872 /// Used to find the error value of an error.
873 module: *Module,
874 /// The list of bytes that have been generated so far,
875 /// can be used to calculate the offset into a section.
876 code: *std.ArrayList(u8),
877
878 /// Sets `err_msg` on `DeclGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig
879 fn fail(self: *DeclGen, comptime fmt: []const u8, args: anytype) InnerError {
880 const src: LazySrcLoc = .{ .node_offset = 0 };
881 const src_loc = src.toSrcLoc(self.decl);
882 self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args);
883 return error.CodegenFail;
884 }
885
886 fn target(self: *const DeclGen) std.Target {
887 return self.bin_file.base.options.target;
888 }
889
890 pub fn genDecl(self: *DeclGen) InnerError!Result {
891 const decl = self.decl;
892 assert(decl.has_tv);
893
894 log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val });
895
896 if (decl.val.castTag(.function)) |func_payload| {
897 _ = func_payload;
898 return self.fail("TODO wasm backend genDecl function pointer", .{});
899 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {
900 const ext_decl = extern_fn.data;
901 var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target());
902 func_type.deinit(self.gpa);
903 ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
904 return Result{ .appended = {} };
905 } else {
906 const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
907 break :init_val payload.data.init;
908 } else decl.val;
909 if (init_val.tag() != .unreachable_value) {
910 return self.genTypedValue(decl.ty, init_val, self.code.writer());
911 }
912 return Result{ .appended = {} };
856913 }
857 return Result.appended;
858914 }
859}
860915
861/// Generates the wasm bytecode for the declaration belonging to `Context`
862fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result {
863 if (val.isUndef()) {
864 try self.code.appendNTimes(0xaa, @intCast(usize, ty.abiSize(self.target)));
865 return Result.appended;
866 }
867 switch (ty.zigTypeTag()) {
868 .Fn => {
869 const fn_decl = switch (val.tag()) {
870 .extern_fn => val.castTag(.extern_fn).?.data,
871 .function => val.castTag(.function).?.data.owner_decl,
916 /// Generates the wasm bytecode for the declaration belonging to `Context`
917 fn genTypedValue(self: *DeclGen, ty: Type, val: Value, writer: anytype) InnerError!Result {
918 if (val.isUndef()) {
919 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
920 return Result{ .appended = {} };
921 }
922 switch (ty.zigTypeTag()) {
923 .Fn => {
924 const fn_decl = switch (val.tag()) {
925 .extern_fn => val.castTag(.extern_fn).?.data,
926 .function => val.castTag(.function).?.data.owner_decl,
927 else => unreachable,
928 };
929 return try self.lowerDeclRef(ty, val, fn_decl, writer);
930 },
931 .Optional => {
932 var opt_buf: Type.Payload.ElemType = undefined;
933 const payload_type = ty.optionalChild(&opt_buf);
934 const is_pl = !val.isNull();
935 const abi_size = @intCast(usize, ty.abiSize(self.target()));
936 const offset = abi_size - @intCast(usize, payload_type.abiSize(self.target()));
937
938 if (!payload_type.hasCodeGenBits()) {
939 try writer.writeByteNTimes(@boolToInt(is_pl), abi_size);
940 return Result{ .appended = {} };
941 }
942
943 if (ty.isPtrLikeOptional()) {
944 if (val.castTag(.opt_payload)) |payload| {
945 return self.genTypedValue(payload_type, payload.data, writer);
946 } else if (!val.isNull()) {
947 return self.genTypedValue(payload_type, val, writer);
948 } else {
949 try writer.writeByteNTimes(0, abi_size);
950 return Result{ .appended = {} };
951 }
952 }
953
954 // `null-tag` bytes
955 try writer.writeByteNTimes(@boolToInt(is_pl), offset);
956 switch (try self.genTypedValue(
957 payload_type,
958 if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
959 writer,
960 )) {
961 .appended => {},
962 .externally_managed => |payload| try writer.writeAll(payload),
963 }
964 return Result{ .appended = {} };
965 },
966 .Array => switch (val.tag()) {
967 .bytes => {
968 const payload = val.castTag(.bytes).?;
969 return Result{ .externally_managed = payload.data };
970 },
971 .array => {
972 const elem_vals = val.castTag(.array).?.data;
973 const elem_ty = ty.childType();
974 for (elem_vals) |elem_val| {
975 switch (try self.genTypedValue(elem_ty, elem_val, writer)) {
976 .appended => {},
977 .externally_managed => |data| try writer.writeAll(data),
978 }
979 }
980 return Result{ .appended = {} };
981 },
982 .repeated => {
983 const array = val.castTag(.repeated).?.data;
984 const elem_ty = ty.childType();
985 const sentinel = ty.sentinel();
986 const len = ty.arrayLen();
987
988 var index: u32 = 0;
989 while (index < len) : (index += 1) {
990 switch (try self.genTypedValue(elem_ty, array, writer)) {
991 .externally_managed => |data| try writer.writeAll(data),
992 .appended => {},
993 }
994 }
995 if (sentinel) |sentinel_value| {
996 return self.genTypedValue(elem_ty, sentinel_value, writer);
997 }
998 return Result{ .appended = {} };
999 },
1000 .empty_array_sentinel => {
1001 const elem_ty = ty.childType();
1002 const sent_val = ty.sentinel().?;
1003 return self.genTypedValue(elem_ty, sent_val, writer);
1004 },
8721005 else => unreachable,
873 };
874 return try self.lowerDeclRef(ty, val, fn_decl);
875 },
876 .Optional => {
877 var opt_buf: Type.Payload.ElemType = undefined;
878 const payload_type = ty.optionalChild(&opt_buf);
879 if (ty.isPtrLikeOptional()) {
880 if (val.castTag(.opt_payload)) |payload| {
881 return try self.genTypedValue(payload_type, payload.data);
882 } else if (!val.isNull()) {
883 return try self.genTypedValue(payload_type, val);
884 } else {
885 try self.code.appendNTimes(0, @intCast(usize, ty.abiSize(self.target)));
886 return Result.appended;
1006 },
1007 .Int => {
1008 const info = ty.intInfo(self.target());
1009 const abi_size = @intCast(usize, ty.abiSize(self.target()));
1010 if (info.bits <= 64) {
1011 var buf: [8]u8 = undefined;
1012 if (info.signedness == .unsigned) {
1013 std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt());
1014 } else std.mem.writeIntLittle(i64, &buf, val.toSignedInt());
1015 try writer.writeAll(buf[0..abi_size]);
1016 return Result{ .appended = {} };
8871017 }
888 }
889 // `null-tag` byte
890 try self.code.appendNTimes(@boolToInt(!val.isNull()), 4);
891 const pl_result = try self.genTypedValue(
892 payload_type,
893 if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
894 );
895 switch (pl_result) {
896 .appended => {},
897 .externally_managed => |payload| try self.code.appendSlice(payload),
898 }
899 return Result.appended;
900 },
901 .Array => switch (val.tag()) {
902 .bytes => {
903 const payload = val.castTag(.bytes).?;
904 return Result{ .externally_managed = payload.data };
1018 var space: Value.BigIntSpace = undefined;
1019 const bigint = val.toBigInt(&space);
1020 const iterations = @divExact(abi_size, @sizeOf(usize));
1021 for (bigint.limbs) |_, index| {
1022 const limb = bigint.limbs[bigint.limbs.len - index - 1];
1023 try writer.writeIntLittle(usize, limb);
1024 } else if (bigint.limbs.len < iterations) {
1025 // When the value is saved in less limbs than the required
1026 // abi size, we fill the remaining parts with 0's.
1027 var it_left = iterations - bigint.limbs.len;
1028 while (it_left > 0) {
1029 it_left -= 1;
1030 try writer.writeIntLittle(usize, 0);
1031 }
1032 }
1033 return Result{ .appended = {} };
9051034 },
906 .array => {
907 const elem_vals = val.castTag(.array).?.data;
908 const elem_ty = ty.childType();
909 for (elem_vals) |elem_val| {
910 switch (try self.genTypedValue(elem_ty, elem_val)) {
1035 .Enum => {
1036 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
1037 return Result{ .appended = {} };
1038 },
1039 .Bool => {
1040 try writer.writeByte(@boolToInt(val.toBool()));
1041 return Result{ .appended = {} };
1042 },
1043 .Struct => {
1044 const field_vals = val.castTag(.@"struct").?.data;
1045 for (field_vals) |field_val, index| {
1046 const field_ty = ty.structFieldType(index);
1047 if (!field_ty.hasCodeGenBits()) continue;
1048 switch (try self.genTypedValue(field_ty, field_val, writer)) {
9111049 .appended => {},
912 .externally_managed => |data| try self.code.appendSlice(data),
1050 .externally_managed => |payload| try writer.writeAll(payload),
9131051 }
9141052 }
915 return Result.appended;
1053 return Result{ .appended = {} };
9161054 },
917 else => return self.fail("TODO implement genTypedValue for array type value: {s}", .{@tagName(val.tag())}),
918 },
919 .Int => {
920 const info = ty.intInfo(self.target);
921 const abi_size = @intCast(usize, ty.abiSize(self.target));
922 // todo: Implement integer sizes larger than 64bits
923 if (info.bits > 64) return self.fail("TODO: Implement genTypedValue for integer bit size: {d}", .{info.bits});
924 var buf: [8]u8 = undefined;
925 if (info.signedness == .unsigned) {
926 std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt());
927 } else std.mem.writeIntLittle(i64, &buf, val.toSignedInt());
928 try self.code.appendSlice(buf[0..abi_size]);
929 return Result.appended;
930 },
931 .Enum => {
932 try self.emitConstant(val, ty);
933 return Result.appended;
934 },
935 .Bool => {
936 const int_byte: u8 = @boolToInt(val.toBool());
937 try self.code.append(int_byte);
938 return Result.appended;
939 },
940 .Struct => {
941 const field_vals = val.castTag(.@"struct").?.data;
942 for (field_vals) |field_val, index| {
943 const field_ty = ty.structFieldType(index);
944 if (!field_ty.hasCodeGenBits()) continue;
945 switch (try self.genTypedValue(field_ty, field_val)) {
946 .appended => {},
947 .externally_managed => |payload| try self.code.appendSlice(payload),
948 }
949 }
950 return Result.appended;
951 },
952 .Union => {
953 // TODO: Implement Union declarations
954 const abi_size = @intCast(usize, ty.abiSize(self.target));
955 try self.code.appendNTimes(0xaa, abi_size);
956 return Result.appended;
957 },
958 .Pointer => switch (val.tag()) {
959 .variable => {
960 const decl = val.castTag(.variable).?.data.owner_decl;
961 return try self.lowerDeclRef(ty, val, decl);
1055 .Union => {
1056 // TODO: Implement Union declarations
1057 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
1058 return Result{ .appended = {} };
9621059 },
963 .decl_ref => {
964 const decl = val.castTag(.decl_ref).?.data;
965 return try self.lowerDeclRef(ty, val, decl);
1060 .Pointer => switch (val.tag()) {
1061 .variable => {
1062 const decl = val.castTag(.variable).?.data.owner_decl;
1063 return self.lowerDeclRef(ty, val, decl, writer);
1064 },
1065 .decl_ref => {
1066 const decl = val.castTag(.decl_ref).?.data;
1067 return self.lowerDeclRef(ty, val, decl, writer);
1068 },
1069 .slice => {
1070 const slice = val.castTag(.slice).?.data;
1071 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1072 const ptr_ty = ty.slicePtrFieldType(&buf);
1073 switch (try self.genTypedValue(ptr_ty, slice.ptr, writer)) {
1074 .externally_managed => |data| try writer.writeAll(data),
1075 .appended => {},
1076 }
1077 switch (try self.genTypedValue(Type.usize, slice.len, writer)) {
1078 .externally_managed => |data| try writer.writeAll(data),
1079 .appended => {},
1080 }
1081 return Result{ .appended = {} };
1082 },
1083 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),
9661084 },
967 .slice => {
968 const slice = val.castTag(.slice).?.data;
969 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
970 const ptr_ty = ty.slicePtrFieldType(&buf);
971 switch (try self.genTypedValue(ptr_ty, slice.ptr)) {
972 .externally_managed => |data| try self.code.appendSlice(data),
1085 .ErrorUnion => {
1086 const error_ty = ty.errorUnionSet();
1087 const payload_ty = ty.errorUnionPayload();
1088 const is_pl = val.errorUnionIsPayload();
1089
1090 const err_val = if (!is_pl) val else Value.initTag(.zero);
1091 switch (try self.genTypedValue(error_ty, err_val, writer)) {
1092 .externally_managed => |data| try writer.writeAll(data),
9731093 .appended => {},
9741094 }
975 switch (try self.genTypedValue(Type.usize, slice.len)) {
976 .externally_managed => |data| try self.code.appendSlice(data),
977 .appended => {},
978 }
979 return Result.appended;
980 },
981 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),
982 },
983 .ErrorUnion => {
984 const error_ty = ty.errorUnionSet();
985 const payload_ty = ty.errorUnionPayload();
986 const is_pl = val.errorUnionIsPayload();
987
988 const err_val = if (!is_pl) val else Value.initTag(.zero);
989 switch (try self.genTypedValue(error_ty, err_val)) {
990 .externally_managed => |data| try self.code.appendSlice(data),
991 .appended => {},
992 }
9931095
994 if (payload_ty.hasCodeGenBits()) {
995 const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef);
996 switch (try self.genTypedValue(payload_ty, pl_val)) {
997 .externally_managed => |data| try self.code.appendSlice(data),
998 .appended => {},
1096 if (payload_ty.hasCodeGenBits()) {
1097 const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef);
1098 switch (try self.genTypedValue(payload_ty, pl_val, writer)) {
1099 .externally_managed => |data| try writer.writeAll(data),
1100 .appended => {},
1101 }
9991102 }
1000 }
10011103
1002 return Result.appended;
1003 },
1004 .ErrorSet => {
1005 switch (val.tag()) {
1006 .@"error" => {
1007 const name = val.castTag(.@"error").?.data.name;
1008 const value = self.global_error_set.get(name).?;
1009 try self.code.writer().writeIntLittle(u32, value);
1010 },
1011 else => {
1012 const abi_size = @intCast(usize, ty.abiSize(self.target));
1013 try self.code.appendNTimes(0, abi_size);
1014 },
1015 }
1016 return Result.appended;
1017 },
1018 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
1104 return Result{ .appended = {} };
1105 },
1106 .ErrorSet => {
1107 switch (val.tag()) {
1108 .@"error" => {
1109 const name = val.castTag(.@"error").?.data.name;
1110 const kv = try self.module.getErrorValue(name);
1111 try writer.writeIntLittle(u32, kv.value);
1112 },
1113 else => {
1114 try writer.writeByteNTimes(0, @intCast(usize, ty.abiSize(self.target())));
1115 },
1116 }
1117 return Result{ .appended = {} };
1118 },
1119 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
1120 }
10191121 }
1020}
10211122
1022fn lowerDeclRef(self: *Self, ty: Type, val: Value, decl: *Module.Decl) InnerError!Result {
1023 if (ty.isSlice()) {
1024 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1025 const slice_ty = ty.slicePtrFieldType(&buf);
1026 switch (try self.genTypedValue(slice_ty, val)) {
1027 .appended => {},
1028 .externally_managed => |payload| try self.code.appendSlice(payload),
1123 fn lowerDeclRef(self: *DeclGen, ty: Type, val: Value, decl: *Module.Decl, writer: anytype) InnerError!Result {
1124 if (ty.isSlice()) {
1125 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1126 const slice_ty = ty.slicePtrFieldType(&buf);
1127 switch (try self.genTypedValue(slice_ty, val, writer)) {
1128 .appended => {},
1129 .externally_managed => |payload| try writer.writeAll(payload),
1130 }
1131 var slice_len: Value.Payload.U64 = .{
1132 .base = .{ .tag = .int_u64 },
1133 .data = val.sliceLen(),
1134 };
1135 return self.genTypedValue(Type.usize, Value.initPayload(&slice_len.base), writer);
10291136 }
1030 var slice_len: Value.Payload.U64 = .{
1031 .base = .{ .tag = .int_u64 },
1032 .data = val.sliceLen(),
1033 };
1034 return try self.genTypedValue(Type.usize, Value.initPayload(&slice_len.base));
1035 }
1036
1037 const offset = @intCast(u32, self.code.items.len);
1038 const atom = &self.decl.link.wasm;
1039 const target_sym_index = decl.link.wasm.sym_index;
1040 decl.markAlive();
1041 if (decl.ty.zigTypeTag() == .Fn) {
1042 // We found a function pointer, so add it to our table,
1043 // as function pointers are not allowed to be stored inside the data section,
1044 // but rather in a function table which are called by index
1045 try self.bin_file.addTableFunction(target_sym_index);
1046 try atom.relocs.append(self.gpa, .{
1047 .index = target_sym_index,
1048 .offset = offset,
1049 .relocation_type = .R_WASM_TABLE_INDEX_I32,
1050 });
1051 } else {
1052 try atom.relocs.append(self.gpa, .{
1053 .index = target_sym_index,
1054 .offset = offset,
1055 .relocation_type = .R_WASM_MEMORY_ADDR_I32,
1056 });
1057 }
1058 const ptr_width = @intCast(usize, self.target.cpu.arch.ptrBitWidth() / 8);
1059 try self.code.appendNTimes(0xaa, ptr_width);
10601137
1061 return Result.appended;
1062}
1138 decl.markAlive();
1139 try writer.writeIntLittle(u32, try self.bin_file.getDeclVAddr(
1140 self.decl, // The decl containing the source symbol index
1141 decl.ty, // type we generate the address of
1142 self.symbol_index, // source symbol index
1143 decl.link.wasm.sym_index, // target symbol index
1144 @intCast(u32, self.code.items.len), // offset
1145 ));
1146 return Result{ .appended = {} };
1147 }
1148};
10631149
10641150const CallWValues = struct {
10651151 args: []WValue,
......@@ -1084,7 +1170,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
10841170 const ret_ty = fn_ty.fnReturnType();
10851171 // Check if we store the result as a pointer to the stack rather than
10861172 // by value
1087 if (self.isByRef(ret_ty)) {
1173 if (isByRef(ret_ty, self.target)) {
10881174 // the sret arg will be passed as first argument, therefore we
10891175 // set the `return_value` before allocating locals for regular args.
10901176 result.return_value = .{ .local = self.local_index };
......@@ -1160,16 +1246,10 @@ fn allocStack(self: *Self, ty: Type) !WValue {
11601246 assert(ty.hasCodeGenBits());
11611247
11621248 // calculate needed stack space
1163 var abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch {
1249 const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch {
11641250 return self.fail("Given type '{}' too big to fit into stack frame", .{ty});
11651251 };
11661252
1167 // We store slices as a struct with a pointer field and a length field
1168 // both being 'usize' size.
1169 if (ty.isSlice()) {
1170 abi_size = self.ptrSize() * 2;
1171 }
1172
11731253 // allocate a local using wasm's pointer size
11741254 const local = try self.allocLocal(Type.@"usize");
11751255 try self.moveStack(abi_size, local.local);
......@@ -1185,7 +1265,6 @@ fn toWasmIntBits(bits: u16) ?u16 {
11851265
11861266/// Performs a copy of bytes for a given type. Copying all bytes
11871267/// from rhs to lhs.
1188/// Asserts `lhs` and `rhs` have their active tag set to `local`
11891268///
11901269/// TODO: Perform feature detection and when bulk_memory is available,
11911270/// use wasm's mem.copy instruction.
......@@ -1194,9 +1273,9 @@ fn memCopy(self: *Self, ty: Type, lhs: WValue, rhs: WValue) !void {
11941273 var offset: u32 = 0;
11951274 while (offset < abi_size) : (offset += 1) {
11961275 // get lhs' address to store the result
1197 try self.addLabel(.local_get, lhs.local);
1276 try self.emitWValue(lhs);
11981277 // load byte from rhs' adress
1199 try self.addLabel(.local_get, rhs.local);
1278 try self.emitWValue(rhs);
12001279 try self.addMemArg(.i32_load8_u, .{ .offset = offset, .alignment = 1 });
12011280 // store the result in lhs (we already have its address on the stack)
12021281 try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 });
......@@ -1207,9 +1286,13 @@ fn ptrSize(self: *const Self) u16 {
12071286 return @divExact(self.target.cpu.arch.ptrBitWidth(), 8);
12081287}
12091288
1289fn arch(self: *const Self) std.Target.Cpu.Arch {
1290 return self.target.cpu.arch;
1291}
1292
12101293/// For a given `Type`, will return true when the type will be passed
1211/// by reference, rather than by value.
1212fn isByRef(self: Self, ty: Type) bool {
1294/// by reference, rather than by value
1295fn isByRef(ty: Type, target: std.Target) bool {
12131296 switch (ty.zigTypeTag()) {
12141297 .Type,
12151298 .ComptimeInt,
......@@ -1237,7 +1320,7 @@ fn isByRef(self: Self, ty: Type) bool {
12371320 .Frame,
12381321 .Union,
12391322 => return ty.hasCodeGenBits(),
1240 .Int => return if (ty.intInfo(self.target).bits > 64) true else false,
1323 .Int => return if (ty.intInfo(target).bits > 64) true else false,
12411324 .ErrorUnion => {
12421325 const has_tag = ty.errorUnionSet().hasCodeGenBits();
12431326 const has_pl = ty.errorUnionPayload().hasCodeGenBits();
......@@ -1442,13 +1525,13 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
14421525fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
14431526 for (body) |inst| {
14441527 const result = try self.genInst(inst);
1445 try self.values.putNoClobber(self.gpa, inst, result);
1528 try self.values.putNoClobber(self.gpa, Air.indexToRef(inst), result);
14461529 }
14471530}
14481531
14491532fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14501533 const un_op = self.air.instructions.items(.data)[inst].un_op;
1451 const operand = self.resolveInst(un_op);
1534 const operand = try self.resolveInst(un_op);
14521535 // result must be stored in the stack and we return a pointer
14531536 // to the stack instead
14541537 if (self.return_value != .none) {
......@@ -1465,7 +1548,7 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14651548 const child_type = self.air.typeOfIndex(inst).childType();
14661549 if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} };
14671550
1468 if (self.isByRef(child_type)) {
1551 if (isByRef(child_type, self.target)) {
14691552 return self.return_value;
14701553 }
14711554
......@@ -1478,11 +1561,11 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14781561
14791562fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14801563 const un_op = self.air.instructions.items(.data)[inst].un_op;
1481 const operand = self.resolveInst(un_op);
1564 const operand = try self.resolveInst(un_op);
14821565 const ret_ty = self.air.typeOf(un_op).childType();
14831566 if (!ret_ty.hasCodeGenBits()) return WValue.none;
14841567
1485 if (!self.isByRef(ret_ty)) {
1568 if (!isByRef(ret_ty, self.target)) {
14861569 const result = try self.load(operand, ret_ty, 0);
14871570 try self.emitWValue(result);
14881571 }
......@@ -1504,7 +1587,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
15041587 else => unreachable,
15051588 };
15061589 const ret_ty = fn_ty.fnReturnType();
1507 const first_param_sret = self.isByRef(ret_ty);
1590 const first_param_sret = isByRef(ret_ty, self.target);
15081591
15091592 const target: ?*Decl = blk: {
15101593 const func_val = self.air.value(pl_op.operand) orelse break :blk null;
......@@ -1525,24 +1608,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
15251608
15261609 for (args) |arg| {
15271610 const arg_ref = @intToEnum(Air.Inst.Ref, arg);
1528 const arg_val = self.resolveInst(arg_ref);
1611 const arg_val = try self.resolveInst(arg_ref);
15291612
15301613 const arg_ty = self.air.typeOf(arg_ref);
15311614 if (!arg_ty.hasCodeGenBits()) continue;
1532
1533 // If we need to pass by reference, but the argument is a constant,
1534 // we must first lower it before passing it.
1535 if (self.isByRef(arg_ty) and arg_val == .constant) {
1536 const arg_local = try self.allocStack(arg_ty);
1537 try self.store(arg_local, arg_val, arg_ty, 0);
1538 try self.emitWValue(arg_local);
1539 } else if (arg_val == .none) {
1540 // TODO: Remove this branch when zero-sized pointers do not generate
1541 // an argument.
1542 try self.addImm32(0);
1543 } else {
1544 try self.emitWValue(arg_val);
1545 }
1615 try self.emitWValue(arg_val);
15461616 }
15471617
15481618 if (target) |direct| {
......@@ -1551,10 +1621,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
15511621 // in this case we call a function pointer
15521622 // so load its value onto the stack
15531623 std.debug.assert(ty.zigTypeTag() == .Pointer);
1554 const operand = self.resolveInst(pl_op.operand);
1624 const operand = try self.resolveInst(pl_op.operand);
15551625 try self.emitWValue(operand);
15561626
1557 var fn_type = try self.genFunctype(fn_ty);
1627 var fn_type = try genFunctype(self.gpa, fn_ty, self.target);
15581628 defer fn_type.deinit(self.gpa);
15591629
15601630 const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type);
......@@ -1595,148 +1665,43 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
15951665fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
15961666 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
15971667
1598 const lhs = self.resolveInst(bin_op.lhs);
1599 const rhs = self.resolveInst(bin_op.rhs);
1668 const lhs = try self.resolveInst(bin_op.lhs);
1669 const rhs = try self.resolveInst(bin_op.rhs);
16001670 const ty = self.air.typeOf(bin_op.lhs).childType();
16011671
1602 const offset: u32 = switch (lhs) {
1603 .local_with_offset => |with_off| with_off.offset,
1604 else => 0,
1605 };
1606
1607 try self.store(lhs, rhs, ty, offset);
1672 try self.store(lhs, rhs, ty, 0);
16081673 return .none;
16091674}
16101675
16111676fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
16121677 switch (ty.zigTypeTag()) {
1613 .ErrorUnion, .Optional => {
1614 var buf: Type.Payload.ElemType = undefined;
1615 const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf);
1616 const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8);
1617 const payload_offset = if (ty.zigTypeTag() == .ErrorUnion)
1618 @intCast(u32, tag_ty.abiSize(self.target))
1619 else if (ty.isPtrLikeOptional())
1620 @as(u32, 0)
1621 else
1622 @intCast(u32, ty.abiSize(self.target) - payload_ty.abiSize(self.target));
1623
1624 switch (rhs) {
1625 .constant => {
1626 if (rhs.constant.val.castTag(.decl_ref)) |_| {
1627 // retrieve values from memory instead
1628 const mem_local = try self.allocLocal(Type.usize);
1629 try self.emitWValue(rhs);
1630 try self.addLabel(.local_set, mem_local.local);
1631 try self.store(lhs, mem_local, ty, 0);
1632 return;
1633 } else if (ty.isPtrLikeOptional()) {
1634 // set the address of rhs to lhs
1635 try self.store(lhs, rhs, Type.usize, 0);
1636 return;
1637 }
1638 // constant will contain both tag and payload,
1639 // so save those in 2 temporary locals before storing them
1640 // in memory
1641 try self.emitWValue(rhs);
1642 const tag_local = try self.allocLocal(tag_ty);
1643
1644 if (payload_ty.hasCodeGenBits()) {
1645 const payload_local = try self.allocLocal(payload_ty);
1646 try self.addLabel(.local_set, payload_local.local);
1647 if (self.isByRef(payload_ty)) {
1648 const ptr = try self.buildPointerOffset(lhs, payload_offset, .new);
1649 try self.store(ptr, payload_local, payload_ty, 0);
1650 } else {
1651 try self.store(lhs, payload_local, payload_ty, payload_offset);
1652 }
1653 }
1654 try self.addLabel(.local_set, tag_local.local);
1678 .ErrorUnion => {
1679 const err_ty = ty.errorUnionSet();
1680 const pl_ty = ty.errorUnionPayload();
1681 if (!pl_ty.hasCodeGenBits()) {
1682 const err_val = try self.load(rhs, err_ty, 0);
1683 return self.store(lhs, err_val, err_ty, 0);
1684 }
16551685
1656 try self.store(lhs, tag_local, tag_ty, 0);
1657 return;
1658 },
1659 .local => {
1660 // When the optional is pointer-like, we simply store the pointer
1661 // instead.
1662 if (ty.isPtrLikeOptional()) {
1663 try self.store(lhs, rhs, Type.usize, 0);
1664 return;
1665 }
1666 // Load values from `rhs` stack position and store in `lhs` instead
1667 const tag_local = try self.load(rhs, tag_ty, 0);
1668 if (payload_ty.hasCodeGenBits()) {
1669 if (self.isByRef(payload_ty)) {
1670 const payload_ptr = try self.buildPointerOffset(rhs, payload_offset, .new);
1671 const lhs_payload_ptr = try self.buildPointerOffset(lhs, payload_offset, .new);
1672 try self.store(lhs_payload_ptr, payload_ptr, payload_ty, 0);
1673 } else {
1674 const payload_local = try self.load(rhs, payload_ty, payload_offset);
1675 try self.store(lhs, payload_local, payload_ty, payload_offset);
1676 }
1677 }
1678 return try self.store(lhs, tag_local, tag_ty, 0);
1679 },
1680 .local_with_offset => |with_offset| {
1681 // check if we're storing the payload, or the error
1682 if (with_offset.offset == 0) {
1683 try self.store(lhs, .{ .local = with_offset.local }, tag_ty, 0);
1684 return;
1685 }
1686 const tag_local = try self.allocLocal(tag_ty);
1687 try self.addImm32(0);
1688 try self.addLabel(.local_set, tag_local.local);
1689 try self.store(lhs, tag_local, tag_ty, 0);
1690
1691 return try self.store(
1692 lhs,
1693 .{ .local = with_offset.local },
1694 payload_ty,
1695 with_offset.offset,
1696 );
1697 },
1698 else => unreachable,
1686 return try self.memCopy(ty, lhs, rhs);
1687 },
1688 .Optional => {
1689 if (ty.isPtrLikeOptional()) {
1690 return self.store(lhs, rhs, Type.usize, 0);
16991691 }
1692 var buf: Type.Payload.ElemType = undefined;
1693 const pl_ty = ty.optionalChild(&buf);
1694 if (!pl_ty.hasCodeGenBits()) {
1695 return self.store(lhs, rhs, Type.initTag(.u8), 0);
1696 }
1697
1698 return self.memCopy(ty, lhs, rhs);
17001699 },
17011700 .Struct, .Array => {
1702 const final_rhs = if (rhs == .constant) blk: {
1703 const tmp = try self.allocLocal(Type.usize);
1704 try self.emitWValue(rhs);
1705 try self.addLabel(.local_set, tmp.local);
1706 break :blk tmp;
1707 } else rhs;
1708 return try self.memCopy(ty, lhs, final_rhs);
1701 return try self.memCopy(ty, lhs, rhs);
17091702 },
17101703 .Pointer => {
1711 if (ty.isSlice() and rhs == .constant) {
1712 try self.emitWValue(rhs);
1713
1714 const val = rhs.constant.val;
1715 const len_local = try self.allocLocal(Type.usize);
1716 const ptr_local = try self.allocLocal(Type.usize);
1717 const len_offset = self.ptrSize();
1718 if (val.castTag(.decl_ref)) |decl| {
1719 const decl_ty: Type = decl.data.ty;
1720 if (decl_ty.isSlice()) {
1721 // for decl references we also need to retrieve the length and the original decl's pointer
1722 try self.addMemArg(.i32_load, .{ .offset = 0, .alignment = self.ptrSize() });
1723 try self.addLabel(.memory_address, decl.data.link.wasm.sym_index);
1724 try self.addMemArg(.i32_load, .{ .offset = len_offset, .alignment = self.ptrSize() });
1725 } else if (decl_ty.zigTypeTag() == .Array) {
1726 const len = decl_ty.arrayLen();
1727 switch (self.ptrSize()) {
1728 4 => try self.addImm32(@bitCast(i32, @intCast(u32, len))),
1729 8 => try self.addImm64(len),
1730 else => unreachable,
1731 }
1732 } else return self.fail("Wasm todo: Implement storing slices for decl_ref with type: {}", .{decl_ty});
1733 }
1734 try self.addLabel(.local_set, len_local.local);
1735 try self.addLabel(.local_set, ptr_local.local);
1736 try self.store(lhs, ptr_local, Type.usize, 0);
1737 try self.store(lhs, len_local, Type.usize, len_offset);
1738 return;
1739 } else if (ty.isSlice()) {
1704 if (ty.isSlice()) {
17401705 // store pointer first
17411706 const ptr_local = try self.load(rhs, Type.usize, 0);
17421707 try self.store(lhs, ptr_local, Type.usize, 0);
......@@ -1748,18 +1713,13 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
17481713 }
17491714 },
17501715 .Int => if (ty.intInfo(self.target).bits > 64) {
1751 if (rhs == .constant) {
1752 try self.emitWValue(rhs);
1753 try self.addLabel(.local_set, lhs.local);
1754 return;
1755 }
17561716 return try self.memCopy(ty, lhs, rhs);
17571717 },
17581718 else => {},
17591719 }
17601720 try self.emitWValue(lhs);
17611721 try self.emitWValue(rhs);
1762 const valtype = try self.typeToValtype(ty);
1722 const valtype = typeToValtype(ty, self.target);
17631723 // check if we should pass by pointer or value based on ABI size
17641724 // TODO: Implement a way to get ABI values from a given type,
17651725 // that is portable across the backend, rather than copying logic.
......@@ -1787,21 +1747,18 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
17871747
17881748fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
17891749 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1790 const operand = self.resolveInst(ty_op.operand);
1750 const operand = try self.resolveInst(ty_op.operand);
17911751 const ty = self.air.getRefType(ty_op.ty);
17921752
17931753 if (!ty.hasCodeGenBits()) return WValue{ .none = {} };
17941754
1795 if (self.isByRef(ty)) {
1755 if (isByRef(ty, self.target)) {
17961756 const new_local = try self.allocStack(ty);
17971757 try self.store(new_local, operand, ty, 0);
17981758 return new_local;
17991759 }
18001760
1801 return switch (operand) {
1802 .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset),
1803 else => try self.load(operand, ty, 0),
1804 };
1761 return self.load(operand, ty, 0);
18051762}
18061763
18071764fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
......@@ -1832,7 +1789,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
18321789 };
18331790
18341791 const opcode = buildOpcode(.{
1835 .valtype1 = try self.typeToValtype(ty),
1792 .valtype1 = typeToValtype(ty, self.target),
18361793 .width = abi_size * 8, // use bitsize instead of byte size
18371794 .op = .load,
18381795 .signedness = signedness,
......@@ -1859,11 +1816,11 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
18591816 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
18601817
18611818 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1862 const lhs = self.resolveInst(bin_op.lhs);
1863 const rhs = self.resolveInst(bin_op.rhs);
1819 const lhs = try self.resolveInst(bin_op.lhs);
1820 const rhs = try self.resolveInst(bin_op.rhs);
18641821 const operand_ty = self.air.typeOfIndex(inst);
18651822
1866 if (self.isByRef(operand_ty)) {
1823 if (isByRef(operand_ty, self.target)) {
18671824 return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty});
18681825 }
18691826
......@@ -1873,7 +1830,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
18731830 const bin_ty = self.air.typeOf(bin_op.lhs);
18741831 const opcode: wasm.Opcode = buildOpcode(.{
18751832 .op = op,
1876 .valtype1 = try self.typeToValtype(bin_ty),
1833 .valtype1 = typeToValtype(bin_ty, self.target),
18771834 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
18781835 });
18791836 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
......@@ -1886,8 +1843,8 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
18861843
18871844fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
18881845 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1889 const lhs = self.resolveInst(bin_op.lhs);
1890 const rhs = self.resolveInst(bin_op.rhs);
1846 const lhs = try self.resolveInst(bin_op.lhs);
1847 const rhs = try self.resolveInst(bin_op.rhs);
18911848
18921849 try self.emitWValue(lhs);
18931850 try self.emitWValue(rhs);
......@@ -1895,7 +1852,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
18951852 const bin_ty = self.air.typeOf(bin_op.lhs);
18961853 const opcode: wasm.Opcode = buildOpcode(.{
18971854 .op = op,
1898 .valtype1 = try self.typeToValtype(bin_ty),
1855 .valtype1 = typeToValtype(bin_ty, self.target),
18991856 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
19001857 });
19011858 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
......@@ -1933,7 +1890,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
19331890 return bin_local;
19341891}
19351892
1936fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
1893fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
19371894 if (val.isUndefDeep()) return self.emitUndefined(ty);
19381895 switch (ty.zigTypeTag()) {
19391896 .Int => {
......@@ -1941,82 +1898,58 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
19411898 // write constant
19421899 switch (int_info.signedness) {
19431900 .signed => switch (int_info.bits) {
1944 0...32 => return try self.addImm32(@intCast(i32, val.toSignedInt())),
1945 33...64 => return try self.addImm64(@bitCast(u64, val.toSignedInt())),
1946 65...128 => {},
1947 else => |bits| return self.fail("Wasm todo: emitConstant for integer with {d} bits", .{bits}),
1901 0...32 => return WValue{ .imm32 = @bitCast(u32, @intCast(i32, val.toSignedInt())) },
1902 33...64 => return WValue{ .imm64 = @bitCast(u64, val.toSignedInt()) },
1903 else => unreachable,
19481904 },
19491905 .unsigned => switch (int_info.bits) {
1950 0...32 => return try self.addImm32(@bitCast(i32, @intCast(u32, val.toUnsignedInt()))),
1951 33...64 => return try self.addImm64(val.toUnsignedInt()),
1952 65...128 => {},
1953 else => |bits| return self.fail("Wasm TODO: emitConstant for integer with {d} bits", .{bits}),
1906 0...32 => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
1907 33...64 => return WValue{ .imm64 = val.toUnsignedInt() },
1908 else => unreachable,
19541909 },
19551910 }
1956 const result = try self.allocStack(ty);
1957 var space: Value.BigIntSpace = undefined;
1958 const bigint = val.toBigInt(&space);
1959 if (bigint.limbs.len == 1 and bigint.limbs[0] == 0) {
1960 try self.addLabel(.local_get, result.local);
1961 return;
1962 }
1963 if (@sizeOf(usize) != @sizeOf(u64)) {
1964 return self.fail("Wasm todo: Implement big integers for 32bit compiler", .{});
1965 }
1966
1967 for (bigint.limbs) |_, index| {
1968 const limb = bigint.limbs[bigint.limbs.len - index - 1];
1969 try self.addLabel(.local_get, result.local);
1970 try self.addImm64(limb);
1971 try self.addMemArg(.i64_store, .{ .offset = @intCast(u32, index * 8), .alignment = 8 });
1972 }
1973 try self.addLabel(.local_get, result.local);
19741911 },
1975 .Bool => try self.addImm32(@intCast(i32, val.toSignedInt())),
1976 .Float => {
1977 // write constant
1978 switch (ty.floatBits(self.target)) {
1979 0...32 => try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val.toFloat(f32) } }),
1980 64 => try self.addFloat64(val.toFloat(f64)),
1981 else => |bits| return self.fail("Wasm TODO: emitConstant for float with {d} bits", .{bits}),
1982 }
1912 .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
1913 .Float => switch (ty.floatBits(self.target)) {
1914 0...32 => return WValue{ .float32 = val.toFloat(f32) },
1915 33...64 => return WValue{ .float64 = val.toFloat(f64) },
1916 else => unreachable,
19831917 },
1984 .Pointer => {
1985 if (val.castTag(.slice)) |slice| {
1986 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1987 try self.emitConstant(slice.data.ptr, ty.slicePtrFieldType(&buf));
1988 try self.emitConstant(slice.data.len, Type.usize);
1989 } else if (val.castTag(.decl_ref)) |payload| {
1990 const decl = payload.data;
1918 .Pointer => switch (val.tag()) {
1919 .decl_ref => {
1920 const decl = val.castTag(.decl_ref).?.data;
19911921 decl.markAlive();
1992 // Function pointers use a table index, rather than a memory address
1993 if (decl.ty.zigTypeTag() == .Fn) {
1994 const target_sym_index = decl.link.wasm.sym_index;
1922 const target_sym_index = decl.link.wasm.sym_index;
1923 if (ty.isSlice()) {
1924 var slice_len: Value.Payload.U64 = .{
1925 .base = .{ .tag = .int_u64 },
1926 .data = val.sliceLen(),
1927 };
1928 var slice_val: Value.Payload.Slice = .{
1929 .base = .{ .tag = .slice },
1930 .data = .{ .ptr = val.slicePtr(), .len = Value.initPayload(&slice_len.base) },
1931 };
1932 return self.lowerConstant(Value.initPayload(&slice_val.base), ty);
1933 } else if (decl.ty.zigTypeTag() == .Fn) {
19951934 try self.bin_file.addTableFunction(target_sym_index);
1996 try self.addLabel(.function_index, target_sym_index);
1997 } else {
1998 try self.addLabel(.memory_address, decl.link.wasm.sym_index);
1999 }
2000 } else if (val.castTag(.int_u64)) |int_ptr| {
2001 try self.addImm32(@bitCast(i32, @intCast(u32, int_ptr.data)));
2002 } else if (val.tag() == .zero or val.tag() == .null_value) {
2003 try self.addImm32(0);
2004 } else if (val.tag() == .one) {
2005 try self.addImm32(1);
2006 } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()});
2007 },
2008 .Void => {},
1935 return WValue{ .function_index = target_sym_index };
1936 } else return WValue{ .memory = target_sym_index };
1937 },
1938 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
1939 .zero, .null_value => return WValue{ .imm32 = 0 },
1940 else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}),
1941 },
20091942 .Enum => {
20101943 if (val.castTag(.enum_field_index)) |field_index| {
20111944 switch (ty.tag()) {
2012 .enum_simple => try self.addImm32(@bitCast(i32, field_index.data)),
1945 .enum_simple => return WValue{ .imm32 = field_index.data },
20131946 .enum_full, .enum_nonexhaustive => {
20141947 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
20151948 if (enum_full.values.count() != 0) {
20161949 const tag_val = enum_full.values.keys()[field_index.data];
2017 try self.emitConstant(tag_val, enum_full.tag_ty);
1950 return self.lowerConstant(tag_val, enum_full.tag_ty);
20181951 } else {
2019 try self.addImm32(@bitCast(i32, field_index.data));
1952 return WValue{ .imm32 = field_index.data };
20201953 }
20211954 },
20221955 else => unreachable,
......@@ -2024,169 +1957,63 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
20241957 } else {
20251958 var int_tag_buffer: Type.Payload.Bits = undefined;
20261959 const int_tag_ty = ty.intTagType(&int_tag_buffer);
2027 try self.emitConstant(val, int_tag_ty);
1960 return self.lowerConstant(val, int_tag_ty);
20281961 }
20291962 },
2030 .ErrorSet => {
2031 const error_index = self.global_error_set.get(val.getError().?).?;
2032 try self.addImm32(@bitCast(i32, error_index));
1963 .ErrorSet => switch (val.tag()) {
1964 .@"error" => {
1965 const kv = try self.module.getErrorValue(val.getError().?);
1966 return WValue{ .imm32 = kv.value };
1967 },
1968 else => return WValue{ .imm32 = 0 },
20331969 },
20341970 .ErrorUnion => {
20351971 const error_type = ty.errorUnionSet();
2036 const payload_type = ty.errorUnionPayload();
2037 if (val.castTag(.eu_payload)) |pl| {
2038 const payload_val = pl.data;
2039 // no error, so write a '0' const
2040 try self.addImm32(0);
2041
2042 if (payload_type.hasCodeGenBits()) {
2043 // after the error code, we emit the payload
2044 try self.emitConstant(payload_val, payload_type);
2045 }
2046 } else {
2047 // write the error val
2048 try self.emitConstant(val, error_type);
2049
2050 if (payload_type.hasCodeGenBits()) {
2051 // no payload, so write a '0' const
2052 try self.addImm32(0);
2053 }
2054 }
1972 const is_pl = val.errorUnionIsPayload();
1973 const err_val = if (!is_pl) val else Value.initTag(.zero);
1974 return self.lowerConstant(err_val, error_type);
20551975 },
2056 .Optional => {
1976 .Optional => if (ty.isPtrLikeOptional()) {
20571977 var buf: Type.Payload.ElemType = undefined;
2058 const payload_type = ty.optionalChild(&buf);
2059 if (ty.isPtrLikeOptional()) {
2060 try self.emitConstant(val, payload_type);
2061 return;
2062 }
2063
2064 // When constant has value 'null', set is_null local to '1'
2065 // and payload to '0'
2066 if (val.castTag(.opt_payload)) |payload| {
2067 try self.addImm32(1);
2068 if (payload_type.hasCodeGenBits())
2069 try self.emitConstant(payload.data, payload_type);
2070 } else {
2071 // set null-tag
2072 try self.addImm32(0);
2073 // null-tag is set, so write a '0' const
2074 try self.addImm32(0);
2075 }
2076 },
2077 .Struct => {
2078 const struct_data = val.castTag(.@"struct").?;
2079 // in case of structs, we reserve stack space and store it there.
2080 const result = try self.allocStack(ty);
2081
2082 const fields = ty.structFields();
2083 const offset = try self.copyLocal(result, ty);
2084 for (fields.values()) |field, index| {
2085 const tmp = try self.allocLocal(field.ty);
2086 try self.emitConstant(struct_data.data[index], field.ty);
2087 try self.addLabel(.local_set, tmp.local);
2088 try self.store(offset, tmp, field.ty, 0);
2089
2090 // this prevents us from emitting useless instructions when we reached the end of the loop
2091 if (index != (fields.count() - 1)) {
2092 _ = try self.buildPointerOffset(offset, field.ty.abiSize(self.target), .modify);
2093 }
2094 }
2095 try self.addLabel(.local_get, result.local);
2096 },
2097 .Array => {
2098 const result = try self.allocStack(ty);
2099 if (val.castTag(.bytes)) |bytes| {
2100 for (bytes.data) |byte, index| {
2101 try self.addLabel(.local_get, result.local);
2102 try self.addImm32(@intCast(i32, byte));
2103 try self.addMemArg(.i32_store8, .{ .offset = @intCast(u32, index), .alignment = 1 });
2104 }
2105 } else if (val.castTag(.array)) |array| {
2106 const elem_ty = ty.childType();
2107 const elem_size = elem_ty.abiSize(self.target);
2108 const tmp = try self.allocLocal(elem_ty);
2109 const offset = try self.copyLocal(result, ty);
2110 for (array.data) |value, index| {
2111 try self.emitConstant(value, elem_ty);
2112 try self.addLabel(.local_set, tmp.local);
2113 try self.store(offset, tmp, elem_ty, 0);
2114
2115 if (index != (array.data.len - 1)) {
2116 _ = try self.buildPointerOffset(offset, elem_size, .modify);
2117 }
2118 }
2119 } else if (val.castTag(.repeated)) |repeated| {
2120 const value = repeated.data;
2121 const elem_ty = ty.childType();
2122 const elem_size = elem_ty.abiSize(self.target);
2123 const sentinel = ty.sentinel();
2124 const len = ty.arrayLen();
2125 const len_with_sent = len + @boolToInt(sentinel != null);
2126 const tmp = try self.allocLocal(elem_ty);
2127 const offset = try self.copyLocal(result, ty);
2128
2129 var index: u32 = 0;
2130 while (index < len_with_sent) : (index += 1) {
2131 if (sentinel != null and index == len) {
2132 try self.emitConstant(sentinel.?, elem_ty);
2133 } else {
2134 try self.emitConstant(value, elem_ty);
2135 }
2136 try self.addLabel(.local_set, tmp.local);
2137 try self.store(offset, tmp, elem_ty, 0);
2138
2139 if (index != (len_with_sent - 1)) {
2140 _ = try self.buildPointerOffset(offset, elem_size, .modify);
2141 }
2142 }
2143 } else if (val.tag() == .empty_array_sentinel) {
2144 const elem_ty = ty.childType();
2145 const sent_val = ty.sentinel().?;
2146 const tmp = try self.allocLocal(elem_ty);
2147 try self.emitConstant(sent_val, elem_ty);
2148 try self.addLabel(.local_set, tmp.local);
2149 try self.store(result, tmp, elem_ty, 0);
2150 } else unreachable;
2151 try self.addLabel(.local_get, result.local);
1978 return self.lowerConstant(val, ty.optionalChild(&buf));
1979 } else {
1980 const is_pl = val.tag() == .opt_payload;
1981 return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 };
21521982 },
2153 else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}),
1983 else => |zig_type| return self.fail("Wasm TODO: LowerConstant for zigTypeTag {s}", .{zig_type}),
21541984 }
21551985}
21561986
2157fn emitUndefined(self: *Self, ty: Type) InnerError!void {
1987fn emitUndefined(self: *Self, ty: Type) InnerError!WValue {
21581988 switch (ty.zigTypeTag()) {
1989 .Bool, .ErrorSet => return WValue{ .imm32 = 0xaaaaaaaa },
21591990 .Int => switch (ty.intInfo(self.target).bits) {
2160 0...32 => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))),
2161 33...64 => try self.addImm64(0xaaaaaaaaaaaaaaaa),
2162 else => |bits| return self.fail("Wasm TODO: emitUndefined for integer bitsize: {d}", .{bits}),
1991 0...32 => return WValue{ .imm32 = 0xaaaaaaaa },
1992 33...64 => return WValue{ .imm64 = 0xaaaaaaaaaaaaaaaa },
1993 else => unreachable,
21631994 },
21641995 .Float => switch (ty.floatBits(self.target)) {
2165 0...32 => try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) } }),
2166 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))),
2167 else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}),
2168 },
2169 .Array, .Struct => {
2170 const result = try self.allocStack(ty);
2171 const abi_size = ty.abiSize(self.target);
2172 var offset: u32 = 0;
2173 while (offset < abi_size) : (offset += 1) {
2174 try self.emitWValue(result);
2175 try self.addImm32(0xaa);
2176 switch (self.ptrSize()) {
2177 4 => try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 }),
2178 8 => try self.addMemArg(.i64_store8, .{ .offset = offset, .alignment = 1 }),
2179 else => unreachable,
2180 }
2181 }
2182 try self.addLabel(.local_get, result.local);
1996 0...32 => return WValue{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) },
1997 33...64 => return WValue{ .float64 = @bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa)) },
1998 else => unreachable,
21831999 },
2184 .Pointer => switch (self.ptrSize()) {
2185 4 => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))),
2186 8 => try self.addImm64(0xaaaaaaaaaaaaaaaa),
2000 .Pointer => switch (self.arch()) {
2001 .wasm32 => return WValue{ .imm32 = 0xaaaaaaaa },
2002 .wasm64 => return WValue{ .imm64 = 0xaaaaaaaaaaaaaaaa },
21872003 else => unreachable,
21882004 },
2189 else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}),
2005 .Optional => {
2006 var buf: Type.Payload.ElemType = undefined;
2007 const pl_ty = ty.optionalChild(&buf);
2008 if (ty.isPtrLikeOptional()) {
2009 return self.emitUndefined(pl_ty);
2010 }
2011 return WValue{ .imm32 = 0xaaaaaaaa };
2012 },
2013 .ErrorUnion => {
2014 return WValue{ .imm32 = 0xaaaaaaaa };
2015 },
2016 else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag()}),
21902017 }
21912018}
21922019
......@@ -2219,8 +2046,8 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
22192046 .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt())),
22202047 },
22212048 .ErrorSet => {
2222 const error_index = self.global_error_set.get(val.getError().?).?;
2223 return @bitCast(i32, error_index);
2049 const kv = self.module.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function
2050 return @bitCast(i32, kv.value);
22242051 },
22252052 else => unreachable, // Programmer called this function for an illegal type
22262053 }
......@@ -2228,7 +2055,7 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
22282055
22292056fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
22302057 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2231 const block_ty = try self.genBlockType(self.air.getRefType(ty_pl.ty));
2058 const block_ty = genBlockType(self.air.getRefType(ty_pl.ty), self.target);
22322059 const extra = self.air.extraData(Air.Block, ty_pl.payload);
22332060 const body = self.air.extra[extra.end..][0..extra.data.body_len];
22342061
......@@ -2285,7 +2112,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
22852112
22862113fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
22872114 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2288 const condition = self.resolveInst(pl_op.operand);
2115 const condition = try self.resolveInst(pl_op.operand);
22892116 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
22902117 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
22912118 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
......@@ -2312,8 +2139,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
23122139
23132140fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue {
23142141 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2315 const lhs = self.resolveInst(bin_op.lhs);
2316 const rhs = self.resolveInst(bin_op.rhs);
2142 const lhs = try self.resolveInst(bin_op.lhs);
2143 const rhs = try self.resolveInst(bin_op.rhs);
23172144 const operand_ty = self.air.typeOf(bin_op.lhs);
23182145
23192146 if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) {
......@@ -2325,7 +2152,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
23252152 // both lhs and rhs, as well as checking the payload are matching of lhs and rhs
23262153 return self.cmpOptionals(lhs, rhs, operand_ty, op);
23272154 }
2328 } else if (self.isByRef(operand_ty)) {
2155 } else if (isByRef(operand_ty, self.target)) {
23292156 return self.cmpBigInt(lhs, rhs, operand_ty, op);
23302157 }
23312158
......@@ -2340,7 +2167,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
23402167 break :blk operand_ty.intInfo(self.target).signedness;
23412168 };
23422169 const opcode: wasm.Opcode = buildOpcode(.{
2343 .valtype1 = try self.typeToValtype(operand_ty),
2170 .valtype1 = typeToValtype(operand_ty, self.target),
23442171 .op = switch (op) {
23452172 .lt => .lt,
23462173 .lte => .le,
......@@ -2364,7 +2191,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
23642191
23652192 // if operand has codegen bits we should break with a value
23662193 if (self.air.typeOf(br.operand).hasCodeGenBits()) {
2367 try self.emitWValue(self.resolveInst(br.operand));
2194 try self.emitWValue(try self.resolveInst(br.operand));
23682195
23692196 if (block.value != .none) {
23702197 try self.addLabel(.local_set, block.value.local);
......@@ -2382,7 +2209,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
23822209fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
23832210 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
23842211
2385 const operand = self.resolveInst(ty_op.operand);
2212 const operand = try self.resolveInst(ty_op.operand);
23862213 try self.emitWValue(operand);
23872214
23882215 // wasm does not have booleans nor the `not` instruction, therefore compare with 0
......@@ -2412,20 +2239,14 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
24122239
24132240fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
24142241 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2415 const operand = self.resolveInst(ty_op.operand);
2416 if (operand == .constant) {
2417 const result = try self.allocLocal(self.air.typeOfIndex(inst));
2418 try self.emitWValue(operand);
2419 try self.addLabel(.local_set, result.local);
2420 return result;
2421 }
2242 const operand = try self.resolveInst(ty_op.operand);
24222243 return operand;
24232244}
24242245
24252246fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
24262247 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
24272248 const extra = self.air.extraData(Air.StructField, ty_pl.payload);
2428 const struct_ptr = self.resolveInst(extra.data.struct_operand);
2249 const struct_ptr = try self.resolveInst(extra.data.struct_operand);
24292250 const struct_ty = self.air.typeOf(extra.data.struct_operand).childType();
24302251 const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) catch {
24312252 return self.fail("Field type '{}' too big to fit into stack frame", .{
......@@ -2437,7 +2258,7 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
24372258
24382259fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue {
24392260 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2440 const struct_ptr = self.resolveInst(ty_op.operand);
2261 const struct_ptr = try self.resolveInst(ty_op.operand);
24412262 const struct_ty = self.air.typeOf(ty_op.operand).childType();
24422263 const field_ty = struct_ty.structFieldType(index);
24432264 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch {
......@@ -2449,47 +2270,35 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr
24492270}
24502271
24512272fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValue {
2452 var final_offset = offset;
2453 const local = switch (struct_ptr) {
2454 .local => |local| local,
2455 .local_with_offset => |with_offset| blk: {
2456 final_offset += with_offset.offset;
2457 break :blk with_offset.local;
2458 },
2459 else => unreachable,
2460 };
2461 return self.buildPointerOffset(.{ .local = local }, final_offset, .new);
2273 return self.buildPointerOffset(struct_ptr, offset, .new);
24622274}
24632275
24642276fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2465 if (self.liveness.isUnused(inst)) return WValue.none;
2277 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
24662278
24672279 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
24682280 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
24692281 const struct_ty = self.air.typeOf(struct_field.struct_operand);
2470 const operand = self.resolveInst(struct_field.struct_operand);
2282 const operand = try self.resolveInst(struct_field.struct_operand);
24712283 const field_index = struct_field.field_index;
24722284 const field_ty = struct_ty.structFieldType(field_index);
2473 if (!field_ty.hasCodeGenBits()) return WValue.none;
2285 if (!field_ty.hasCodeGenBits()) return WValue{ .none = {} };
24742286 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch {
24752287 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
24762288 };
24772289
2478 if (self.isByRef(field_ty)) {
2479 return WValue{ .local_with_offset = .{ .local = operand.local, .offset = offset } };
2290 if (isByRef(field_ty, self.target)) {
2291 return self.buildPointerOffset(operand, offset, .new);
24802292 }
24812293
2482 switch (operand) {
2483 .local_with_offset => |with_offset| return try self.load(operand, field_ty, offset + with_offset.offset),
2484 else => return try self.load(operand, field_ty, offset),
2485 }
2294 return self.load(operand, field_ty, offset);
24862295}
24872296
24882297fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
24892298 // result type is always 'noreturn'
24902299 const blocktype = wasm.block_empty;
24912300 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2492 const target = self.resolveInst(pl_op.operand);
2301 const target = try self.resolveInst(pl_op.operand);
24932302 const target_ty = self.air.typeOf(pl_op.operand);
24942303 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
24952304 var extra_index: usize = switch_br.end;
......@@ -2595,9 +2404,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
25952404 // for single value prong we can emit a simple if
25962405 if (case.values.len == 1) {
25972406 try self.emitWValue(target);
2598 try self.emitConstant(case.values[0].value, target_ty);
2407 const val = try self.lowerConstant(case.values[0].value, target_ty);
2408 try self.emitWValue(val);
25992409 const opcode = buildOpcode(.{
2600 .valtype1 = try self.typeToValtype(target_ty),
2410 .valtype1 = typeToValtype(target_ty, self.target),
26012411 .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition.
26022412 .signedness = signedness,
26032413 });
......@@ -2608,9 +2418,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26082418 try self.startBlock(.block, blocktype);
26092419 for (case.values) |value| {
26102420 try self.emitWValue(target);
2611 try self.emitConstant(value.value, target_ty);
2421 const val = try self.lowerConstant(value.value, target_ty);
2422 try self.emitWValue(val);
26122423 const opcode = buildOpcode(.{
2613 .valtype1 = try self.typeToValtype(target_ty),
2424 .valtype1 = typeToValtype(target_ty, self.target),
26142425 .op = .eq,
26152426 .signedness = signedness,
26162427 });
......@@ -2635,7 +2446,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26352446
26362447fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
26372448 const un_op = self.air.instructions.items(.data)[inst].un_op;
2638 const operand = self.resolveInst(un_op);
2449 const operand = try self.resolveInst(un_op);
26392450 const err_ty = self.air.typeOf(un_op);
26402451 const pl_ty = err_ty.errorUnionPayload();
26412452
......@@ -2660,12 +2471,12 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
26602471fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26612472 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
26622473 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2663 const operand = self.resolveInst(ty_op.operand);
2474 const operand = try self.resolveInst(ty_op.operand);
26642475 const err_ty = self.air.typeOf(ty_op.operand);
26652476 const payload_ty = err_ty.errorUnionPayload();
26662477 if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} };
26672478 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target));
2668 if (self.isByRef(payload_ty)) {
2479 if (isByRef(payload_ty, self.target)) {
26692480 return self.buildPointerOffset(operand, offset, .new);
26702481 }
26712482 return try self.load(operand, payload_ty, offset);
......@@ -2675,7 +2486,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26752486 if (self.liveness.isUnused(inst)) return WValue.none;
26762487
26772488 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2678 const operand = self.resolveInst(ty_op.operand);
2489 const operand = try self.resolveInst(ty_op.operand);
26792490 const err_ty = self.air.typeOf(ty_op.operand);
26802491 const payload_ty = err_ty.errorUnionPayload();
26812492 if (!payload_ty.hasCodeGenBits()) {
......@@ -2688,7 +2499,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26882499fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26892500 if (self.liveness.isUnused(inst)) return WValue.none;
26902501 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2691 const operand = self.resolveInst(ty_op.operand);
2502 const operand = try self.resolveInst(ty_op.operand);
26922503
26932504 const op_ty = self.air.typeOf(ty_op.operand);
26942505 if (!op_ty.hasCodeGenBits()) return operand;
......@@ -2710,7 +2521,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27102521fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27112522 if (self.liveness.isUnused(inst)) return WValue.none;
27122523 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2713 const operand = self.resolveInst(ty_op.operand);
2524 const operand = try self.resolveInst(ty_op.operand);
27142525 const err_ty = self.air.getRefType(ty_op.ty);
27152526
27162527 const err_union = try self.allocStack(err_ty);
......@@ -2724,7 +2535,7 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27242535
27252536 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
27262537 const ty = self.air.getRefType(ty_op.ty);
2727 const operand = self.resolveInst(ty_op.operand);
2538 const operand = try self.resolveInst(ty_op.operand);
27282539 const ref_ty = self.air.typeOf(ty_op.operand);
27292540 const ref_info = ref_ty.intInfo(self.target);
27302541 const wanted_info = ty.intInfo(self.target);
......@@ -2755,7 +2566,7 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27552566
27562567fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!WValue {
27572568 const un_op = self.air.instructions.items(.data)[inst].un_op;
2758 const operand = self.resolveInst(un_op);
2569 const operand = try self.resolveInst(un_op);
27592570
27602571 const op_ty = self.air.typeOf(un_op);
27612572 const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty;
......@@ -2786,7 +2597,7 @@ fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode)
27862597fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27872598 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
27882599 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2789 const operand = self.resolveInst(ty_op.operand);
2600 const operand = try self.resolveInst(ty_op.operand);
27902601 const opt_ty = self.air.typeOf(ty_op.operand);
27912602 const payload_ty = self.air.typeOfIndex(inst);
27922603 if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} };
......@@ -2794,7 +2605,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27942605
27952606 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);
27962607
2797 if (self.isByRef(payload_ty)) {
2608 if (isByRef(payload_ty, self.target)) {
27982609 return self.buildPointerOffset(operand, offset, .new);
27992610 }
28002611
......@@ -2805,7 +2616,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28052616 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
28062617
28072618 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2808 const operand = self.resolveInst(ty_op.operand);
2619 const operand = try self.resolveInst(ty_op.operand);
28092620 const opt_ty = self.air.typeOf(ty_op.operand).childType();
28102621
28112622 var buf: Type.Payload.ElemType = undefined;
......@@ -2820,7 +2631,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28202631
28212632fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28222633 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2823 const operand = self.resolveInst(ty_op.operand);
2634 const operand = try self.resolveInst(ty_op.operand);
28242635 const opt_ty = self.air.typeOf(ty_op.operand).childType();
28252636 var buf: Type.Payload.ElemType = undefined;
28262637 const payload_ty = opt_ty.optionalChild(&buf);
......@@ -2856,7 +2667,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28562667 return non_null_bit;
28572668 }
28582669
2859 const operand = self.resolveInst(ty_op.operand);
2670 const operand = try self.resolveInst(ty_op.operand);
28602671 const op_ty = self.air.typeOfIndex(inst);
28612672 if (op_ty.isPtrLikeOptional()) {
28622673 return operand;
......@@ -2882,8 +2693,8 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28822693
28832694 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
28842695 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
2885 const lhs = self.resolveInst(bin_op.lhs);
2886 const rhs = self.resolveInst(bin_op.rhs);
2696 const lhs = try self.resolveInst(bin_op.lhs);
2697 const rhs = try self.resolveInst(bin_op.rhs);
28872698 const slice_ty = self.air.typeOfIndex(inst);
28882699
28892700 const slice = try self.allocStack(slice_ty);
......@@ -2897,7 +2708,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28972708 if (self.liveness.isUnused(inst)) return WValue.none;
28982709
28992710 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2900 const operand = self.resolveInst(ty_op.operand);
2711 const operand = try self.resolveInst(ty_op.operand);
29012712
29022713 return try self.load(operand, Type.usize, self.ptrSize());
29032714}
......@@ -2907,8 +2718,8 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29072718
29082719 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
29092720 const slice_ty = self.air.typeOf(bin_op.lhs);
2910 const slice = self.resolveInst(bin_op.lhs);
2911 const index = self.resolveInst(bin_op.rhs);
2721 const slice = try self.resolveInst(bin_op.lhs);
2722 const index = try self.resolveInst(bin_op.rhs);
29122723 const elem_ty = slice_ty.childType();
29132724 const elem_size = elem_ty.abiSize(self.target);
29142725
......@@ -2925,7 +2736,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29252736 const result = try self.allocLocal(elem_ty);
29262737 try self.addLabel(.local_set, result.local);
29272738
2928 if (self.isByRef(elem_ty)) {
2739 if (isByRef(elem_ty, self.target)) {
29292740 return result;
29302741 }
29312742 return try self.load(result, elem_ty, 0);
......@@ -2939,8 +2750,8 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29392750 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
29402751 const elem_size = elem_ty.abiSize(self.target);
29412752
2942 const slice = self.resolveInst(bin_op.lhs);
2943 const index = self.resolveInst(bin_op.rhs);
2753 const slice = try self.resolveInst(bin_op.lhs);
2754 const index = try self.resolveInst(bin_op.rhs);
29442755
29452756 const slice_ptr = try self.load(slice, slice_ty, 0);
29462757 try self.addLabel(.local_get, slice_ptr.local);
......@@ -2959,14 +2770,14 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29592770fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29602771 if (self.liveness.isUnused(inst)) return WValue.none;
29612772 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2962 const operand = self.resolveInst(ty_op.operand);
2773 const operand = try self.resolveInst(ty_op.operand);
29632774 return try self.load(operand, Type.usize, 0);
29642775}
29652776
29662777fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29672778 if (self.liveness.isUnused(inst)) return WValue.none;
29682779 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2969 const operand = self.resolveInst(ty_op.operand);
2780 const operand = try self.resolveInst(ty_op.operand);
29702781 const op_ty = self.air.typeOf(ty_op.operand);
29712782 const int_info = self.air.getRefType(ty_op.ty).intInfo(self.target);
29722783 const wanted_bits = int_info.bits;
......@@ -3025,12 +2836,12 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30252836
30262837fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30272838 const un_op = self.air.instructions.items(.data)[inst].un_op;
3028 return self.resolveInst(un_op);
2839 return try self.resolveInst(un_op);
30292840}
30302841
30312842fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30322843 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3033 const operand = self.resolveInst(ty_op.operand);
2844 const operand = try self.resolveInst(ty_op.operand);
30342845 const array_ty = self.air.typeOf(ty_op.operand).childType();
30352846 const ty = Type.@"usize";
30362847 const ptr_width = @intCast(u32, ty.abiSize(self.target));
......@@ -3057,7 +2868,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30572868fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30582869 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
30592870 const un_op = self.air.instructions.items(.data)[inst].un_op;
3060 return self.resolveInst(un_op);
2871 return try self.resolveInst(un_op);
30612872}
30622873
30632874fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3065,8 +2876,8 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30652876
30662877 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
30672878 const ptr_ty = self.air.typeOf(bin_op.lhs);
3068 const pointer = self.resolveInst(bin_op.lhs);
3069 const index = self.resolveInst(bin_op.rhs);
2879 const pointer = try self.resolveInst(bin_op.lhs);
2880 const index = try self.resolveInst(bin_op.rhs);
30702881 const elem_ty = ptr_ty.childType();
30712882 const elem_size = elem_ty.abiSize(self.target);
30722883
......@@ -3086,7 +2897,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30862897
30872898 const result = try self.allocLocal(elem_ty);
30882899 try self.addLabel(.local_set, result.local);
3089 if (self.isByRef(elem_ty)) {
2900 if (isByRef(elem_ty, self.target)) {
30902901 return result;
30912902 }
30922903 return try self.load(result, elem_ty, 0);
......@@ -3100,8 +2911,8 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
31002911 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
31012912 const elem_size = elem_ty.abiSize(self.target);
31022913
3103 const ptr = self.resolveInst(bin_op.lhs);
3104 const index = self.resolveInst(bin_op.rhs);
2914 const ptr = try self.resolveInst(bin_op.lhs);
2915 const index = try self.resolveInst(bin_op.rhs);
31052916
31062917 // load pointer onto the stack
31072918 if (ptr_ty.isSlice()) {
......@@ -3125,15 +2936,15 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
31252936fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
31262937 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
31272938 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3128 const ptr = self.resolveInst(bin_op.lhs);
3129 const offset = self.resolveInst(bin_op.rhs);
2939 const ptr = try self.resolveInst(bin_op.lhs);
2940 const offset = try self.resolveInst(bin_op.rhs);
31302941 const ptr_ty = self.air.typeOf(bin_op.lhs);
31312942 const pointee_ty = switch (ptr_ty.ptrSize()) {
31322943 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
31332944 else => ptr_ty.childType(),
31342945 };
31352946
3136 const valtype = try self.typeToValtype(Type.usize);
2947 const valtype = typeToValtype(Type.usize, self.target);
31372948 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });
31382949 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });
31392950
......@@ -3152,9 +2963,9 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
31522963 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
31532964 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;
31542965
3155 const ptr = self.resolveInst(pl_op.operand);
3156 const value = self.resolveInst(bin_op.lhs);
3157 const len = self.resolveInst(bin_op.rhs);
2966 const ptr = try self.resolveInst(pl_op.operand);
2967 const value = try self.resolveInst(bin_op.lhs);
2968 const len = try self.resolveInst(bin_op.rhs);
31582969 try self.memSet(ptr, len, value);
31592970
31602971 return WValue.none;
......@@ -3221,8 +3032,8 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
32213032
32223033 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
32233034 const array_ty = self.air.typeOf(bin_op.lhs);
3224 const array = self.resolveInst(bin_op.lhs);
3225 const index = self.resolveInst(bin_op.rhs);
3035 const array = try self.resolveInst(bin_op.lhs);
3036 const index = try self.resolveInst(bin_op.rhs);
32263037 const elem_ty = array_ty.childType();
32273038 const elem_size = elem_ty.abiSize(self.target);
32283039
......@@ -3236,7 +3047,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
32363047 const result = try self.allocLocal(elem_ty);
32373048 try self.addLabel(.local_set, result.local);
32383049
3239 if (self.isByRef(elem_ty)) {
3050 if (isByRef(elem_ty, self.target)) {
32403051 return result;
32413052 }
32423053 return try self.load(result, elem_ty, 0);
......@@ -3246,15 +3057,15 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
32463057 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
32473058
32483059 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3249 const operand = self.resolveInst(ty_op.operand);
3060 const operand = try self.resolveInst(ty_op.operand);
32503061 const dest_ty = self.air.typeOfIndex(inst);
32513062 const op_ty = self.air.typeOf(ty_op.operand);
32523063
32533064 try self.emitWValue(operand);
32543065 const op = buildOpcode(.{
32553066 .op = .trunc,
3256 .valtype1 = try self.typeToValtype(dest_ty),
3257 .valtype2 = try self.typeToValtype(op_ty),
3067 .valtype1 = typeToValtype(dest_ty, self.target),
3068 .valtype2 = typeToValtype(op_ty, self.target),
32583069 .signedness = if (dest_ty.isSignedInt()) .signed else .unsigned,
32593070 });
32603071 try self.addTag(Mir.Inst.Tag.fromOpcode(op));
......@@ -3268,7 +3079,7 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
32683079 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
32693080
32703081 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3271 const operand = self.resolveInst(ty_op.operand);
3082 const operand = try self.resolveInst(ty_op.operand);
32723083
32733084 _ = ty_op;
32743085 _ = operand;
......@@ -3318,7 +3129,7 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
33183129
33193130 try self.emitWValue(lhs_pl);
33203131 try self.emitWValue(rhs_pl);
3321 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = try self.typeToValtype(payload_ty) });
3132 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, self.target) });
33223133 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
33233134 try self.addLabel(.br_if, 0);
33243135
src/arch/wasm/Emit.zig+14-6
......@@ -323,16 +323,24 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
323323
324324fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
325325 const symbol_index = emit.mir.instructions.items(.data)[inst].label;
326 try emit.code.append(std.wasm.opcode(.i32_const));
327 const mem_offset = emit.offset();
328 var buf: [5]u8 = undefined;
329 leb128.writeUnsignedFixed(5, &buf, symbol_index);
330 try emit.code.appendSlice(&buf);
326 const mem_offset = emit.offset() + 1;
327 const is_wasm32 = emit.bin_file.options.target.cpu.arch == .wasm32;
328 if (is_wasm32) {
329 try emit.code.append(std.wasm.opcode(.i32_const));
330 var buf: [5]u8 = undefined;
331 leb128.writeUnsignedFixed(5, &buf, symbol_index);
332 try emit.code.appendSlice(&buf);
333 } else {
334 try emit.code.append(std.wasm.opcode(.i64_const));
335 var buf: [10]u8 = undefined;
336 leb128.writeUnsignedFixed(10, &buf, symbol_index);
337 try emit.code.appendSlice(&buf);
338 }
331339
332340 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
333341 .offset = mem_offset,
334342 .index = symbol_index,
335 .relocation_type = .R_WASM_MEMORY_ADDR_LEB,
343 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,
336344 });
337345}
338346
src/link/Wasm.zig+111-34
......@@ -19,7 +19,7 @@ const trace = @import("../tracy.zig").trace;
1919const build_options = @import("build_options");
2020const wasi_libc = @import("../wasi_libc.zig");
2121const Cache = @import("../Cache.zig");
22const TypedValue = @import("../TypedValue.zig");
22const Type = @import("../type.zig").Type;
2323const LlvmObject = @import("../codegen/llvm.zig").Object;
2424const Air = @import("../Air.zig");
2525const Liveness = @import("../Liveness.zig");
......@@ -234,12 +234,12 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
234234 .locals = .{},
235235 .target = self.base.options.target,
236236 .bin_file = self,
237 .global_error_set = self.base.options.module.?.global_error_set,
237 .module = module,
238238 };
239239 defer codegen.deinit();
240240
241241 // generate the 'code' section for the function declaration
242 const result = codegen.genFunc() catch |err| switch (err) {
242 codegen.genFunc() catch |err| switch (err) {
243243 error.CodegenFail => {
244244 decl.analysis = .codegen_failure;
245245 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
247247 },
248248 else => |e| return e,
249249 };
250 return self.finishUpdateDecl(decl, result, &codegen);
250 return self.finishUpdateDecl(decl, codegen.code.items);
251251}
252252
253253// 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 {
264264
265265 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 = .{
268270 .gpa = self.base.allocator,
269 .air = undefined,
270 .liveness = undefined,
271 .values = .{},
272 .code = std.ArrayList(u8).init(self.base.allocator),
273271 .decl = decl,
274 .err_msg = undefined,
275 .locals = .{},
276 .target = self.base.options.target,
272 .symbol_index = decl.link.wasm.sym_index,
277273 .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,
279277 };
280 defer codegen.deinit();
281278
282279 // 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) {
284281 error.CodegenFail => {
285282 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);
287284 return;
288285 },
289286 else => |e| return e,
290287 };
291288
292 return self.finishUpdateDecl(decl, result, &codegen);
293}
294
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,
289 const code = switch (result) {
290 .externally_managed => |data| data,
291 .appended => code_writer.items,
299292 };
300293
294 return self.finishUpdateDecl(decl, code);
295}
296
297fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
301298 if (decl.isExtern()) {
302299 try self.addOrUpdateImport(decl);
303300 return;
......@@ -306,9 +303,75 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, result: CodeGen.Result, cod
306303 if (code.len == 0) return;
307304 const atom: *Atom = &decl.link.wasm;
308305 atom.size = @intCast(u32, code.len);
306 atom.alignment = decl.ty.abiAlignment(self.base.options.target);
307 self.symbols.items[atom.sym_index].name = decl.name;
309308 try atom.code.appendSlice(self.base.allocator, code);
310309}
311310
311/// Creates a new local symbol for a given type (and its bytes it's represented by)
312/// and then append it as a 'contained' atom onto the Decl.
313pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type) !u32 {
314 assert(ty.zigTypeTag() != .Fn); // cannot create local symbols for functions
315 var symbol: Symbol = .{
316 .name = "unnamed_local",
317 .flags = 0,
318 .tag = .data,
319 .index = undefined,
320 };
321 symbol.setFlag(.WASM_SYM_BINDING_LOCAL);
322 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
323
324 var atom = Atom.empty;
325 atom.alignment = ty.abiAlignment(self.base.options.target);
326 try self.symbols.ensureUnusedCapacity(self.base.allocator, 1);
327
328 if (self.symbols_free_list.popOrNull()) |index| {
329 atom.sym_index = index;
330 self.symbols.items[index] = symbol;
331 } else {
332 atom.sym_index = @intCast(u32, self.symbols.items.len);
333 self.symbols.appendAssumeCapacity(symbol);
334 }
335
336 try decl.link.wasm.locals.append(self.base.allocator, atom);
337 return atom.sym_index;
338}
339
340pub fn updateLocalSymbolCode(self: *Wasm, decl: *Module.Decl, symbol_index: u32, code: []const u8) !void {
341 const atom = decl.link.wasm.symbolAtom(symbol_index);
342 atom.size = @intCast(u32, code.len);
343 try atom.code.appendSlice(self.base.allocator, code);
344}
345
346/// For a given decl, find the given symbol index's atom, and create a relocation for the type.
347/// Returns the given pointer address
348pub fn getDeclVAddr(self: *Wasm, decl: *Module.Decl, ty: Type, symbol_index: u32, target_symbol_index: u32, offset: u32) !u32 {
349 const atom = decl.link.wasm.symbolAtom(symbol_index);
350 const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;
351 if (ty.zigTypeTag() == .Fn) {
352 // We found a function pointer, so add it to our table,
353 // as function pointers are not allowed to be stored inside the data section.
354 // They are instead stored in a function table which are called by index.
355 try self.addTableFunction(target_symbol_index);
356 try atom.relocs.append(self.base.allocator, .{
357 .index = target_symbol_index,
358 .offset = offset,
359 .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
360 });
361 } else {
362 try atom.relocs.append(self.base.allocator, .{
363 .index = target_symbol_index,
364 .offset = offset,
365 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
366 });
367 }
368 // we do not know the final address at this point,
369 // as atom allocation will determine the address and relocations
370 // will calculate and rewrite this. Therefore, we simply return the symbol index
371 // that was targeted.
372 return target_symbol_index;
373}
374
312375pub fn updateDeclExports(
313376 self: *Wasm,
314377 module: *Module,
......@@ -329,9 +392,12 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
329392 }
330393 const atom = &decl.link.wasm;
331394 self.symbols_free_list.append(self.base.allocator, atom.sym_index) catch {};
332 atom.deinit(self.base.allocator);
333395 _ = self.decls.remove(decl);
334396 self.symbols.items[atom.sym_index].tag = .dead; // to ensure it does not end in the names section
397 for (atom.locals.items) |local_atom| {
398 self.symbols.items[local_atom.sym_index].tag = .dead; // also for any local symbol
399 }
400 atom.deinit(self.base.allocator);
335401
336402 if (decl.isExtern()) {
337403 const import = self.imports.fetchRemove(decl.link.wasm.sym_index).?.value;
......@@ -377,14 +443,16 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
377443 }
378444}
379445
380fn parseDeclIntoAtom(self: *Wasm, decl: *Module.Decl) !void {
381 const atom: *Atom = &decl.link.wasm;
446const Kind = union(enum) {
447 data: void,
448 function: FnData,
449};
450
451/// Parses an Atom and inserts its metadata into the corresponding sections.
452fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
382453 const symbol: *Symbol = &self.symbols.items[atom.sym_index];
383 symbol.name = decl.name;
384 atom.alignment = decl.ty.abiAlignment(self.base.options.target);
385 const final_index: u32 = switch (decl.ty.zigTypeTag()) {
386 .Fn => result: {
387 const fn_data = decl.fn_link.wasm;
454 const final_index: u32 = switch (kind) {
455 .function => |fn_data| result: {
388456 const type_index = fn_data.type_index;
389457 const index = @intCast(u32, self.functions.items.len + self.imported_functions_count);
390458 try self.functions.append(self.base.allocator, .{ .type_index = type_index });
......@@ -402,7 +470,7 @@ fn parseDeclIntoAtom(self: *Wasm, decl: *Module.Decl) !void {
402470
403471 break :result self.code_section_index.?;
404472 },
405 else => result: {
473 .data => result: {
406474 const gop = try self.data_segments.getOrPut(self.base.allocator, ".rodata");
407475 const atom_index = if (gop.found_existing) blk: {
408476 self.segments.items[gop.value_ptr.*].size += atom.size;
......@@ -430,7 +498,6 @@ fn parseDeclIntoAtom(self: *Wasm, decl: *Module.Decl) !void {
430498 });
431499 symbol.tag = .data;
432500 symbol.index = info_index;
433 atom.alignment = decl.ty.abiAlignment(self.base.options.target);
434501
435502 break :result atom_index;
436503 },
......@@ -617,7 +684,17 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
617684 var decl_it = self.decls.keyIterator();
618685 while (decl_it.next()) |decl| {
619686 if (decl.*.isExtern()) continue;
620 try self.parseDeclIntoAtom(decl.*);
687 const atom = &decl.*.link.wasm;
688 if (decl.*.ty.zigTypeTag() == .Fn) {
689 try self.parseAtom(atom, .{ .function = decl.*.fn_link.wasm });
690 } else {
691 try self.parseAtom(atom, .data);
692 }
693
694 // also parse atoms for a decl's locals
695 for (atom.locals.items) |*local_atom| {
696 try self.parseAtom(local_atom, .data);
697 }
621698 }
622699
623700 try self.setupMemory();
src/link/Wasm/Atom.zig+18
......@@ -31,6 +31,10 @@ next: ?*Atom,
3131/// is null when this atom is the first in its order
3232prev: ?*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
3438/// Represents a default empty wasm `Atom`
3539pub const empty: Atom = .{
3640 .alignment = 0,
......@@ -45,6 +49,11 @@ pub const empty: Atom = .{
4549pub fn deinit(self: *Atom, gpa: Allocator) void {
4650 self.relocs.deinit(gpa);
4751 self.code.deinit(gpa);
52
53 while (self.locals.popOrNull()) |*local| {
54 local.deinit(gpa);
55 }
56 self.locals.deinit(gpa);
4857}
4958
5059/// Sets the length of relocations and code to '0',
......@@ -72,6 +81,15 @@ pub fn getFirst(self: *Atom) *Atom {
7281 return tmp;
7382}
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
7593/// Resolves the relocations within the atom, writing the new value
7694/// at the calculated offset.
7795pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
test/behavior/array.zig-7
......@@ -146,13 +146,6 @@ test "void arrays" {
146146test "nested arrays" {
147147 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
148148
149 if (builtin.zig_backend == .stage2_wasm) {
150 // TODO this is a recent stage2 test case regression due to an enhancement;
151 // now arrays are properly detected as comptime. This exercised a new code
152 // path in the wasm backend that is not yet implemented.
153 return error.SkipZigTest;
154 }
155
156149 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
157150 for (array_of_strings) |s, i| {
158151 if (i == 0) try expect(mem.eql(u8, s, "hello"));
test/behavior/for.zig-6
......@@ -62,12 +62,6 @@ test "ignore lval with underscore (for loop)" {
6262}
6363
6464test "basic for loop" {
65 if (@import("builtin").zig_backend == .stage2_wasm) {
66 // TODO this is a recent stage2 test case regression due to an enhancement;
67 // now arrays are properly detected as comptime. This exercised a new code
68 // path in the wasm backend that is not yet implemented.
69 return error.SkipZigTest;
70 }
7165 const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3;
7266
7367 var buffer: [expected_result.len]u8 = undefined;