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) {...@@ -27,18 +27,22 @@ const WValue = union(enum) {
27 none: void,27 none: void,
28 /// Index of the local variable28 /// Index of the local variable
29 local: u32,29 local: u32,
30 /// Holds a memoized typed value30 /// An immediate 32bit value
31 constant: TypedValue,31 imm32: u32,
32 /// Used for types that contains of multiple areas within32 /// An immediate 64bit value
33 /// a memory region in the stack.33 imm64: u64,
34 /// The local represents the position in the stack,34 /// A constant 32bit float value
35 /// whereas the offset represents the offset from that position.35 float32: f32,
36 local_with_offset: struct {36 /// A constant 64bit float value
37 /// Index of the local variable37 float64: f64,
38 local: u32,38 /// A value that represents a pointer to the data section
39 /// The offset from the local's stack position39 /// Note: The value contains the symbol index, rather than the actual address
40 offset: u32,40 /// as we use this to perform the relocation.
41 },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,
42};46};
4347
44/// Wasm ops, but without input/output/signedness information48/// Wasm ops, but without input/output/signedness information
...@@ -500,7 +504,7 @@ pub const Result = union(enum) {...@@ -500,7 +504,7 @@ pub const Result = union(enum) {
500};504};
501505
502/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`506/// 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
505const Self = @This();509const Self = @This();
506510
...@@ -538,10 +542,9 @@ locals: std.ArrayListUnmanaged(u8),...@@ -538,10 +542,9 @@ locals: std.ArrayListUnmanaged(u8),
538target: std.Target,542target: std.Target,
539/// Represents the wasm binary file that is being linked.543/// Represents the wasm binary file that is being linked.
540bin_file: *link.File.Wasm,544bin_file: *link.File.Wasm,
541/// Table with the global error set. Consists of every error found in545/// Reference to the Module that this decl is part of.
542/// the compiled code. Each error name maps to a `Module.ErrorInt` which is emitted546/// Used to find the error value.
543/// during codegen to determine the error value.547module: *Module,
544global_error_set: std.StringHashMapUnmanaged(Module.ErrorInt),
545/// List of MIR Instructions548/// List of MIR Instructions
546mir_instructions: std.MultiArrayList(Mir.Inst) = .{},549mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
547/// Contains extra data for MIR550/// Contains extra data for MIR
...@@ -577,7 +580,7 @@ pub fn deinit(self: *Self) void {...@@ -577,7 +580,7 @@ pub fn deinit(self: *Self) void {
577 self.* = undefined;580 self.* = undefined;
578}581}
579582
580/// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig583/// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig
581fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError {584fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError {
582 const src: LazySrcLoc = .{ .node_offset = 0 };585 const src: LazySrcLoc = .{ .node_offset = 0 };
583 const src_loc = src.toSrcLoc(self.decl);586 const src_loc = src.toSrcLoc(self.decl);
...@@ -587,25 +590,52 @@ fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError {...@@ -587,25 +590,52 @@ fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError {
587590
588/// Resolves the `WValue` for the given instruction `inst`591/// Resolves the `WValue` for the given instruction `inst`
589/// When the given instruction has a `Value`, it returns a constant instead592/// When the given instruction has a `Value`, it returns a constant instead
590fn resolveInst(self: Self, ref: Air.Inst.Ref) WValue {593fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
591 const inst_index = Air.refToIndex(ref) orelse {594 const gop = try self.values.getOrPut(self.gpa, ref);
592 const tv = Air.Inst.Ref.typed_value_map[@enumToInt(ref)];595 if (gop.found_existing) return gop.value_ptr.*;
593 if (!tv.ty.hasCodeGenBits()) {596
594 return WValue.none;597 // when we did not find an existing instruction, it
595 }598 // means we must generate it from a constant.
596 return WValue{ .constant = tv };599 const val = self.air.value(ref).?;
597 };600 const ty = self.air.typeOf(ref);
598601 if (!ty.hasCodeGenBits() and !ty.isInt()) return WValue{ .none = {} };
599 const inst_type = self.air.typeOfIndex(inst_index);602
600 // It's allowed to have 0-bit integers603 // When we need to pass the value by reference (such as a struct), we will
601 if (!inst_type.hasCodeGenBits() and !inst_type.isInt()) return WValue{ .none = {} };604 // leverage `genTypedValue` to lower the constant to bytes and emit it
602605 // to the 'rodata' section. We then return the index into the section as `WValue`.
603 if (self.air.instructions.items(.tag)[inst_index] == .constant) {606 //
604 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;607 // In the other cases, we will simply lower the constant to a value that fits
605 return WValue{ .constant = .{ .ty = inst_type, .val = self.air.values[ty_pl.payload] } };608 // into a single local (such as a pointer, integer, bool, etc).
606 }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;
609}639}
610640
611/// Appends a MIR instruction and returns its index within the list of instructions641/// 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 {...@@ -677,60 +707,55 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) error{OutOfMemory}!u32 {
677 return result;707 return result;
678}708}
679709
680/// Using a given `Type`, returns the corresponding wasm Valtype710/// Using a given `Type`, returns the corresponding type
681fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {711fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {
682 return switch (ty.zigTypeTag()) {712 return switch (ty.zigTypeTag()) {
683 .Float => blk: {713 .Float => blk: {
684 const bits = ty.floatBits(self.target);714 const bits = ty.floatBits(target);
685 if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32;715 if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32;
686 if (bits == 64) break :blk wasm.Valtype.f64;716 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
688 },718 },
689 .Int => blk: {719 .Int => blk: {
690 const info = ty.intInfo(self.target);720 const info = ty.intInfo(target);
691 if (info.bits <= 32) break :blk wasm.Valtype.i32;721 if (info.bits <= 32) break :blk wasm.Valtype.i32;
692 if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64;722 if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64;
693 break :blk wasm.Valtype.i32; // represented as pointer to stack723 break :blk wasm.Valtype.i32; // represented as pointer to stack
694 },724 },
695 .Enum => switch (ty.tag()) {725 .Enum => switch (ty.tag()) {
696 .enum_simple => wasm.Valtype.i32,726 .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),
698 },728 },
699 .Bool,729 else => wasm.Valtype.i32, // all represented as reference/immediate
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}),
709 };730 };
710}731}
711732
712/// Using a given `Type`, returns the byte representation of its wasm value type733/// Using a given `Type`, returns the byte representation of its wasm value type
713fn genValtype(self: *Self, ty: Type) InnerError!u8 {734fn genValtype(ty: Type, target: std.Target) u8 {
714 return wasm.valtype(try self.typeToValtype(ty));735 return wasm.valtype(typeToValtype(ty, target));
715}736}
716737
717/// Using a given `Type`, returns the corresponding wasm value type738/// Using a given `Type`, returns the corresponding wasm value type
718/// Differently from `genValtype` this also allows `void` to create a block739/// Differently from `genValtype` this also allows `void` to create a block
719/// with no return type740/// with no return type
720fn genBlockType(self: *Self, ty: Type) InnerError!u8 {741fn genBlockType(ty: Type, target: std.Target) u8 {
721 return switch (ty.tag()) {742 return switch (ty.tag()) {
722 .void, .noreturn => wasm.block_empty,743 .void, .noreturn => wasm.block_empty,
723 else => self.genValtype(ty),744 else => genValtype(ty, target),
724 };745 };
725}746}
726747
727/// Writes the bytecode depending on the given `WValue` in `val`748/// Writes the bytecode depending on the given `WValue` in `val`
728fn emitWValue(self: *Self, val: WValue) InnerError!void {749fn emitWValue(self: *Self, value: WValue) InnerError!void {
729 switch (val) {750 switch (value) {
730 .none => {}, // no-op751 .none => {}, // no-op
731 .local_with_offset => |with_off| try self.addLabel(.local_get, with_off.local),
732 .local => |idx| try self.addLabel(.local_get, idx),752 .local => |idx| try self.addLabel(.local_get, idx),
733 .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack753 .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
734 }759 }
735}760}
736761
...@@ -738,7 +763,7 @@ fn emitWValue(self: *Self, val: WValue) InnerError!void {...@@ -738,7 +763,7 @@ fn emitWValue(self: *Self, val: WValue) InnerError!void {
738/// Returns a corresponding `Wvalue` with `local` as active tag763/// Returns a corresponding `Wvalue` with `local` as active tag
739fn allocLocal(self: *Self, ty: Type) InnerError!WValue {764fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
740 const initial_index = self.local_index;765 const initial_index = self.local_index;
741 const valtype = try self.genValtype(ty);766 const valtype = genValtype(ty, self.target);
742 try self.locals.append(self.gpa, valtype);767 try self.locals.append(self.gpa, valtype);
743 self.local_index += 1;768 self.local_index += 1;
744 return WValue{ .local = initial_index };769 return WValue{ .local = initial_index };
...@@ -746,33 +771,33 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue {...@@ -746,33 +771,33 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
746771
747/// Generates a `wasm.Type` from a given function type.772/// Generates a `wasm.Type` from a given function type.
748/// Memory is owned by the caller.773/// Memory is owned by the caller.
749fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {774fn genFunctype(gpa: Allocator, fn_ty: Type, target: std.Target) !wasm.Type {
750 var params = std.ArrayList(wasm.Valtype).init(self.gpa);775 var params = std.ArrayList(wasm.Valtype).init(gpa);
751 defer params.deinit();776 defer params.deinit();
752 var returns = std.ArrayList(wasm.Valtype).init(self.gpa);777 var returns = std.ArrayList(wasm.Valtype).init(gpa);
753 defer returns.deinit();778 defer returns.deinit();
754 const return_type = fn_ty.fnReturnType();779 const return_type = fn_ty.fnReturnType();
755780
756 const want_sret = self.isByRef(return_type);781 const want_sret = isByRef(return_type, target);
757782
758 if (want_sret) {783 if (want_sret) {
759 try params.append(try self.typeToValtype(Type.usize));784 try params.append(typeToValtype(return_type, target));
760 }785 }
761786
762 // param types787 // param types
763 if (fn_ty.fnParamLen() != 0) {788 if (fn_ty.fnParamLen() != 0) {
764 const fn_params = try self.gpa.alloc(Type, fn_ty.fnParamLen());789 const fn_params = try gpa.alloc(Type, fn_ty.fnParamLen());
765 defer self.gpa.free(fn_params);790 defer gpa.free(fn_params);
766 fn_ty.fnParamTypes(fn_params);791 fn_ty.fnParamTypes(fn_params);
767 for (fn_params) |param_type| {792 for (fn_params) |param_type| {
768 if (!param_type.hasCodeGenBits()) continue;793 if (!param_type.hasCodeGenBits()) continue;
769 try params.append(try self.typeToValtype(param_type));794 try params.append(typeToValtype(param_type, target));
770 }795 }
771 }796 }
772797
773 // return type798 // return type
774 if (!want_sret and return_type.hasCodeGenBits()) {799 if (!want_sret and return_type.hasCodeGenBits()) {
775 try returns.append(try self.typeToValtype(return_type));800 try returns.append(typeToValtype(return_type, target));
776 }801 }
777802
778 return wasm.Type{803 return wasm.Type{
...@@ -781,8 +806,8 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {...@@ -781,8 +806,8 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {
781 };806 };
782}807}
783808
784pub fn genFunc(self: *Self) InnerError!Result {809pub fn genFunc(self: *Self) InnerError!void {
785 var func_type = try self.genFunctype(self.decl.ty);810 var func_type = try genFunctype(self.gpa, self.decl.ty, self.target);
786 defer func_type.deinit(self.gpa);811 defer func_type.deinit(self.gpa);
787 self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);812 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 {...@@ -827,239 +852,300 @@ pub fn genFunc(self: *Self) InnerError!Result {
827 },852 },
828 else => |e| return e,853 else => |e| return e,
829 };854 };
830
831 // codegen data has been appended to `code`
832 return Result.appended;
833}855}
834856
835pub fn genDecl(self: *Self) InnerError!Result {857pub const DeclGen = struct {
836 const decl = self.decl;858 /// The decl we are generating code for.
837 assert(decl.has_tv);859 decl: *Decl,
838860 /// The symbol we're generating code for.
839 log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val });861 /// This can either be the symbol of the Decl itself,
840862 /// or one of its locals.
841 if (decl.val.castTag(.function)) |func_payload| {863 symbol_index: u32,
842 _ = func_payload;864 gpa: Allocator,
843 return self.fail("TODO wasm backend genDecl function pointer", .{});865 /// A reference to the linker, that will process the decl's
844 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {866 /// code and create any relocations it deems neccesary.
845 const ext_decl = extern_fn.data;867 bin_file: *link.File.Wasm,
846 var func_type = try self.genFunctype(ext_decl.ty);868 /// This will be set when `InnerError` has been returned.
847 func_type.deinit(self.gpa);869 /// In any other case, this will be 'undefined'.
848 ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);870 err_msg: *Module.ErrorMsg,
849 return Result.appended;871 /// Reference to the Module that is being compiled.
850 } else {872 /// Used to find the error value of an error.
851 const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {873 module: *Module,
852 break :init_val payload.data.init;874 /// The list of bytes that have been generated so far,
853 } else decl.val;875 /// can be used to calculate the offset into a section.
854 if (init_val.tag() != .unreachable_value) {876 code: *std.ArrayList(u8),
855 return try self.genTypedValue(decl.ty, init_val);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 = {} };
856 }913 }
857 return Result.appended;
858 }914 }
859}
860915
861/// Generates the wasm bytecode for the declaration belonging to `Context`916 /// Generates the wasm bytecode for the declaration belonging to `Context`
862fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result {917 fn genTypedValue(self: *DeclGen, ty: Type, val: Value, writer: anytype) InnerError!Result {
863 if (val.isUndef()) {918 if (val.isUndef()) {
864 try self.code.appendNTimes(0xaa, @intCast(usize, ty.abiSize(self.target)));919 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
865 return Result.appended;920 return Result{ .appended = {} };
866 }921 }
867 switch (ty.zigTypeTag()) {922 switch (ty.zigTypeTag()) {
868 .Fn => {923 .Fn => {
869 const fn_decl = switch (val.tag()) {924 const fn_decl = switch (val.tag()) {
870 .extern_fn => val.castTag(.extern_fn).?.data,925 .extern_fn => val.castTag(.extern_fn).?.data,
871 .function => val.castTag(.function).?.data.owner_decl,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 },
872 else => unreachable,1005 else => unreachable,
873 };1006 },
874 return try self.lowerDeclRef(ty, val, fn_decl);1007 .Int => {
875 },1008 const info = ty.intInfo(self.target());
876 .Optional => {1009 const abi_size = @intCast(usize, ty.abiSize(self.target()));
877 var opt_buf: Type.Payload.ElemType = undefined;1010 if (info.bits <= 64) {
878 const payload_type = ty.optionalChild(&opt_buf);1011 var buf: [8]u8 = undefined;
879 if (ty.isPtrLikeOptional()) {1012 if (info.signedness == .unsigned) {
880 if (val.castTag(.opt_payload)) |payload| {1013 std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt());
881 return try self.genTypedValue(payload_type, payload.data);1014 } else std.mem.writeIntLittle(i64, &buf, val.toSignedInt());
882 } else if (!val.isNull()) {1015 try writer.writeAll(buf[0..abi_size]);
883 return try self.genTypedValue(payload_type, val);1016 return Result{ .appended = {} };
884 } else {
885 try self.code.appendNTimes(0, @intCast(usize, ty.abiSize(self.target)));
886 return Result.appended;
887 }1017 }
888 }1018 var space: Value.BigIntSpace = undefined;
889 // `null-tag` byte1019 const bigint = val.toBigInt(&space);
890 try self.code.appendNTimes(@boolToInt(!val.isNull()), 4);1020 const iterations = @divExact(abi_size, @sizeOf(usize));
891 const pl_result = try self.genTypedValue(1021 for (bigint.limbs) |_, index| {
892 payload_type,1022 const limb = bigint.limbs[bigint.limbs.len - index - 1];
893 if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),1023 try writer.writeIntLittle(usize, limb);
894 );1024 } else if (bigint.limbs.len < iterations) {
895 switch (pl_result) {1025 // When the value is saved in less limbs than the required
896 .appended => {},1026 // abi size, we fill the remaining parts with 0's.
897 .externally_managed => |payload| try self.code.appendSlice(payload),1027 var it_left = iterations - bigint.limbs.len;
898 }1028 while (it_left > 0) {
899 return Result.appended;1029 it_left -= 1;
900 },1030 try writer.writeIntLittle(usize, 0);
901 .Array => switch (val.tag()) {1031 }
902 .bytes => {1032 }
903 const payload = val.castTag(.bytes).?;1033 return Result{ .appended = {} };
904 return Result{ .externally_managed = payload.data };
905 },1034 },
906 .array => {1035 .Enum => {
907 const elem_vals = val.castTag(.array).?.data;1036 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
908 const elem_ty = ty.childType();1037 return Result{ .appended = {} };
909 for (elem_vals) |elem_val| {1038 },
910 switch (try self.genTypedValue(elem_ty, elem_val)) {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)) {
911 .appended => {},1049 .appended => {},
912 .externally_managed => |data| try self.code.appendSlice(data),1050 .externally_managed => |payload| try writer.writeAll(payload),
913 }1051 }
914 }1052 }
915 return Result.appended;1053 return Result{ .appended = {} };
916 },1054 },
917 else => return self.fail("TODO implement genTypedValue for array type value: {s}", .{@tagName(val.tag())}),1055 .Union => {
918 },1056 // TODO: Implement Union declarations
919 .Int => {1057 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
920 const info = ty.intInfo(self.target);1058 return Result{ .appended = {} };
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);
962 },1059 },
963 .decl_ref => {1060 .Pointer => switch (val.tag()) {
964 const decl = val.castTag(.decl_ref).?.data;1061 .variable => {
965 return try self.lowerDeclRef(ty, val, decl);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())}),
966 },1084 },
967 .slice => {1085 .ErrorUnion => {
968 const slice = val.castTag(.slice).?.data;1086 const error_ty = ty.errorUnionSet();
969 var buf: Type.SlicePtrFieldTypeBuffer = undefined;1087 const payload_ty = ty.errorUnionPayload();
970 const ptr_ty = ty.slicePtrFieldType(&buf);1088 const is_pl = val.errorUnionIsPayload();
971 switch (try self.genTypedValue(ptr_ty, slice.ptr)) {1089
972 .externally_managed => |data| try self.code.appendSlice(data),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),
973 .appended => {},1093 .appended => {},
974 }1094 }
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()) {1096 if (payload_ty.hasCodeGenBits()) {
995 const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef);1097 const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef);
996 switch (try self.genTypedValue(payload_ty, pl_val)) {1098 switch (try self.genTypedValue(payload_ty, pl_val, writer)) {
997 .externally_managed => |data| try self.code.appendSlice(data),1099 .externally_managed => |data| try writer.writeAll(data),
998 .appended => {},1100 .appended => {},
1101 }
999 }1102 }
1000 }
10011103
1002 return Result.appended;1104 return Result{ .appended = {} };
1003 },1105 },
1004 .ErrorSet => {1106 .ErrorSet => {
1005 switch (val.tag()) {1107 switch (val.tag()) {
1006 .@"error" => {1108 .@"error" => {
1007 const name = val.castTag(.@"error").?.data.name;1109 const name = val.castTag(.@"error").?.data.name;
1008 const value = self.global_error_set.get(name).?;1110 const kv = try self.module.getErrorValue(name);
1009 try self.code.writer().writeIntLittle(u32, value);1111 try writer.writeIntLittle(u32, kv.value);
1010 },1112 },
1011 else => {1113 else => {
1012 const abi_size = @intCast(usize, ty.abiSize(self.target));1114 try writer.writeByteNTimes(0, @intCast(usize, ty.abiSize(self.target())));
1013 try self.code.appendNTimes(0, abi_size);1115 },
1014 },1116 }
1015 }1117 return Result{ .appended = {} };
1016 return Result.appended;1118 },
1017 },1119 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
1018 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),1120 }
1019 }1121 }
1020}
10211122
1022fn lowerDeclRef(self: *Self, ty: Type, val: Value, decl: *Module.Decl) InnerError!Result {1123 fn lowerDeclRef(self: *DeclGen, ty: Type, val: Value, decl: *Module.Decl, writer: anytype) InnerError!Result {
1023 if (ty.isSlice()) {1124 if (ty.isSlice()) {
1024 var buf: Type.SlicePtrFieldTypeBuffer = undefined;1125 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1025 const slice_ty = ty.slicePtrFieldType(&buf);1126 const slice_ty = ty.slicePtrFieldType(&buf);
1026 switch (try self.genTypedValue(slice_ty, val)) {1127 switch (try self.genTypedValue(slice_ty, val, writer)) {
1027 .appended => {},1128 .appended => {},
1028 .externally_managed => |payload| try self.code.appendSlice(payload),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);
1029 }1136 }
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;1138 decl.markAlive();
1062}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
1064const CallWValues = struct {1150const CallWValues = struct {
1065 args: []WValue,1151 args: []WValue,
...@@ -1084,7 +1170,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu...@@ -1084,7 +1170,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
1084 const ret_ty = fn_ty.fnReturnType();1170 const ret_ty = fn_ty.fnReturnType();
1085 // Check if we store the result as a pointer to the stack rather than1171 // Check if we store the result as a pointer to the stack rather than
1086 // by value1172 // by value
1087 if (self.isByRef(ret_ty)) {1173 if (isByRef(ret_ty, self.target)) {
1088 // the sret arg will be passed as first argument, therefore we1174 // the sret arg will be passed as first argument, therefore we
1089 // set the `return_value` before allocating locals for regular args.1175 // set the `return_value` before allocating locals for regular args.
1090 result.return_value = .{ .local = self.local_index };1176 result.return_value = .{ .local = self.local_index };
...@@ -1160,16 +1246,10 @@ fn allocStack(self: *Self, ty: Type) !WValue {...@@ -1160,16 +1246,10 @@ fn allocStack(self: *Self, ty: Type) !WValue {
1160 assert(ty.hasCodeGenBits());1246 assert(ty.hasCodeGenBits());
11611247
1162 // calculate needed stack space1248 // 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 {
1164 return self.fail("Given type '{}' too big to fit into stack frame", .{ty});1250 return self.fail("Given type '{}' too big to fit into stack frame", .{ty});
1165 };1251 };
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
1173 // allocate a local using wasm's pointer size1253 // allocate a local using wasm's pointer size
1174 const local = try self.allocLocal(Type.@"usize");1254 const local = try self.allocLocal(Type.@"usize");
1175 try self.moveStack(abi_size, local.local);1255 try self.moveStack(abi_size, local.local);
...@@ -1185,7 +1265,6 @@ fn toWasmIntBits(bits: u16) ?u16 {...@@ -1185,7 +1265,6 @@ fn toWasmIntBits(bits: u16) ?u16 {
11851265
1186/// Performs a copy of bytes for a given type. Copying all bytes1266/// Performs a copy of bytes for a given type. Copying all bytes
1187/// from rhs to lhs.1267/// from rhs to lhs.
1188/// Asserts `lhs` and `rhs` have their active tag set to `local`
1189///1268///
1190/// TODO: Perform feature detection and when bulk_memory is available,1269/// TODO: Perform feature detection and when bulk_memory is available,
1191/// use wasm's mem.copy instruction.1270/// use wasm's mem.copy instruction.
...@@ -1194,9 +1273,9 @@ fn memCopy(self: *Self, ty: Type, lhs: WValue, rhs: WValue) !void {...@@ -1194,9 +1273,9 @@ fn memCopy(self: *Self, ty: Type, lhs: WValue, rhs: WValue) !void {
1194 var offset: u32 = 0;1273 var offset: u32 = 0;
1195 while (offset < abi_size) : (offset += 1) {1274 while (offset < abi_size) : (offset += 1) {
1196 // get lhs' address to store the result1275 // get lhs' address to store the result
1197 try self.addLabel(.local_get, lhs.local);1276 try self.emitWValue(lhs);
1198 // load byte from rhs' adress1277 // load byte from rhs' adress
1199 try self.addLabel(.local_get, rhs.local);1278 try self.emitWValue(rhs);
1200 try self.addMemArg(.i32_load8_u, .{ .offset = offset, .alignment = 1 });1279 try self.addMemArg(.i32_load8_u, .{ .offset = offset, .alignment = 1 });
1201 // store the result in lhs (we already have its address on the stack)1280 // store the result in lhs (we already have its address on the stack)
1202 try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 });1281 try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 });
...@@ -1207,9 +1286,13 @@ fn ptrSize(self: *const Self) u16 {...@@ -1207,9 +1286,13 @@ fn ptrSize(self: *const Self) u16 {
1207 return @divExact(self.target.cpu.arch.ptrBitWidth(), 8);1286 return @divExact(self.target.cpu.arch.ptrBitWidth(), 8);
1208}1287}
12091288
1289fn arch(self: *const Self) std.Target.Cpu.Arch {
1290 return self.target.cpu.arch;
1291}
1292
1210/// For a given `Type`, will return true when the type will be passed1293/// For a given `Type`, will return true when the type will be passed
1211/// by reference, rather than by value.1294/// by reference, rather than by value
1212fn isByRef(self: Self, ty: Type) bool {1295fn isByRef(ty: Type, target: std.Target) bool {
1213 switch (ty.zigTypeTag()) {1296 switch (ty.zigTypeTag()) {
1214 .Type,1297 .Type,
1215 .ComptimeInt,1298 .ComptimeInt,
...@@ -1237,7 +1320,7 @@ fn isByRef(self: Self, ty: Type) bool {...@@ -1237,7 +1320,7 @@ fn isByRef(self: Self, ty: Type) bool {
1237 .Frame,1320 .Frame,
1238 .Union,1321 .Union,
1239 => return ty.hasCodeGenBits(),1322 => 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,
1241 .ErrorUnion => {1324 .ErrorUnion => {
1242 const has_tag = ty.errorUnionSet().hasCodeGenBits();1325 const has_tag = ty.errorUnionSet().hasCodeGenBits();
1243 const has_pl = ty.errorUnionPayload().hasCodeGenBits();1326 const has_pl = ty.errorUnionPayload().hasCodeGenBits();
...@@ -1442,13 +1525,13 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1442,13 +1525,13 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1442fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {1525fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1443 for (body) |inst| {1526 for (body) |inst| {
1444 const result = try self.genInst(inst);1527 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);
1446 }1529 }
1447}1530}
14481531
1449fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1532fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1450 const un_op = self.air.instructions.items(.data)[inst].un_op;1533 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);
1452 // result must be stored in the stack and we return a pointer1535 // result must be stored in the stack and we return a pointer
1453 // to the stack instead1536 // to the stack instead
1454 if (self.return_value != .none) {1537 if (self.return_value != .none) {
...@@ -1465,7 +1548,7 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1465,7 +1548,7 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1465 const child_type = self.air.typeOfIndex(inst).childType();1548 const child_type = self.air.typeOfIndex(inst).childType();
1466 if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} };1549 if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} };
14671550
1468 if (self.isByRef(child_type)) {1551 if (isByRef(child_type, self.target)) {
1469 return self.return_value;1552 return self.return_value;
1470 }1553 }
14711554
...@@ -1478,11 +1561,11 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1478,11 +1561,11 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14781561
1479fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1562fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1480 const un_op = self.air.instructions.items(.data)[inst].un_op;1563 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);
1482 const ret_ty = self.air.typeOf(un_op).childType();1565 const ret_ty = self.air.typeOf(un_op).childType();
1483 if (!ret_ty.hasCodeGenBits()) return WValue.none;1566 if (!ret_ty.hasCodeGenBits()) return WValue.none;
14841567
1485 if (!self.isByRef(ret_ty)) {1568 if (!isByRef(ret_ty, self.target)) {
1486 const result = try self.load(operand, ret_ty, 0);1569 const result = try self.load(operand, ret_ty, 0);
1487 try self.emitWValue(result);1570 try self.emitWValue(result);
1488 }1571 }
...@@ -1504,7 +1587,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1504,7 +1587,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1504 else => unreachable,1587 else => unreachable,
1505 };1588 };
1506 const ret_ty = fn_ty.fnReturnType();1589 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
1509 const target: ?*Decl = blk: {1592 const target: ?*Decl = blk: {
1510 const func_val = self.air.value(pl_op.operand) orelse break :blk null;1593 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 {...@@ -1525,24 +1608,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
15251608
1526 for (args) |arg| {1609 for (args) |arg| {
1527 const arg_ref = @intToEnum(Air.Inst.Ref, arg);1610 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
1530 const arg_ty = self.air.typeOf(arg_ref);1613 const arg_ty = self.air.typeOf(arg_ref);
1531 if (!arg_ty.hasCodeGenBits()) continue;1614 if (!arg_ty.hasCodeGenBits()) continue;
15321615 try self.emitWValue(arg_val);
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 }
1546 }1616 }
15471617
1548 if (target) |direct| {1618 if (target) |direct| {
...@@ -1551,10 +1621,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1551,10 +1621,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1551 // in this case we call a function pointer1621 // in this case we call a function pointer
1552 // so load its value onto the stack1622 // so load its value onto the stack
1553 std.debug.assert(ty.zigTypeTag() == .Pointer);1623 std.debug.assert(ty.zigTypeTag() == .Pointer);
1554 const operand = self.resolveInst(pl_op.operand);1624 const operand = try self.resolveInst(pl_op.operand);
1555 try self.emitWValue(operand);1625 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);
1558 defer fn_type.deinit(self.gpa);1628 defer fn_type.deinit(self.gpa);
15591629
1560 const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type);1630 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 {...@@ -1595,148 +1665,43 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1595fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1665fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1596 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1666 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
15971667
1598 const lhs = self.resolveInst(bin_op.lhs);1668 const lhs = try self.resolveInst(bin_op.lhs);
1599 const rhs = self.resolveInst(bin_op.rhs);1669 const rhs = try self.resolveInst(bin_op.rhs);
1600 const ty = self.air.typeOf(bin_op.lhs).childType();1670 const ty = self.air.typeOf(bin_op.lhs).childType();
16011671
1602 const offset: u32 = switch (lhs) {1672 try self.store(lhs, rhs, ty, 0);
1603 .local_with_offset => |with_off| with_off.offset,
1604 else => 0,
1605 };
1606
1607 try self.store(lhs, rhs, ty, offset);
1608 return .none;1673 return .none;
1609}1674}
16101675
1611fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {1676fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
1612 switch (ty.zigTypeTag()) {1677 switch (ty.zigTypeTag()) {
1613 .ErrorUnion, .Optional => {1678 .ErrorUnion => {
1614 var buf: Type.Payload.ElemType = undefined;1679 const err_ty = ty.errorUnionSet();
1615 const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf);1680 const pl_ty = ty.errorUnionPayload();
1616 const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8);1681 if (!pl_ty.hasCodeGenBits()) {
1617 const payload_offset = if (ty.zigTypeTag() == .ErrorUnion)1682 const err_val = try self.load(rhs, err_ty, 0);
1618 @intCast(u32, tag_ty.abiSize(self.target))1683 return self.store(lhs, err_val, err_ty, 0);
1619 else if (ty.isPtrLikeOptional())1684 }
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);
16551685
1656 try self.store(lhs, tag_local, tag_ty, 0);1686 return try self.memCopy(ty, lhs, rhs);
1657 return;1687 },
1658 },1688 .Optional => {
1659 .local => {1689 if (ty.isPtrLikeOptional()) {
1660 // When the optional is pointer-like, we simply store the pointer1690 return self.store(lhs, rhs, Type.usize, 0);
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,
1699 }1691 }
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);
1700 },1699 },
1701 .Struct, .Array => {1700 .Struct, .Array => {
1702 const final_rhs = if (rhs == .constant) blk: {1701 return try self.memCopy(ty, lhs, rhs);
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);
1709 },1702 },
1710 .Pointer => {1703 .Pointer => {
1711 if (ty.isSlice() and rhs == .constant) {1704 if (ty.isSlice()) {
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()) {
1740 // store pointer first1705 // store pointer first
1741 const ptr_local = try self.load(rhs, Type.usize, 0);1706 const ptr_local = try self.load(rhs, Type.usize, 0);
1742 try self.store(lhs, ptr_local, Type.usize, 0);1707 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...@@ -1748,18 +1713,13 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1748 }1713 }
1749 },1714 },
1750 .Int => if (ty.intInfo(self.target).bits > 64) {1715 .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 }
1756 return try self.memCopy(ty, lhs, rhs);1716 return try self.memCopy(ty, lhs, rhs);
1757 },1717 },
1758 else => {},1718 else => {},
1759 }1719 }
1760 try self.emitWValue(lhs);1720 try self.emitWValue(lhs);
1761 try self.emitWValue(rhs);1721 try self.emitWValue(rhs);
1762 const valtype = try self.typeToValtype(ty);1722 const valtype = typeToValtype(ty, self.target);
1763 // check if we should pass by pointer or value based on ABI size1723 // check if we should pass by pointer or value based on ABI size
1764 // TODO: Implement a way to get ABI values from a given type,1724 // TODO: Implement a way to get ABI values from a given type,
1765 // that is portable across the backend, rather than copying logic.1725 // 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...@@ -1787,21 +1747,18 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
17871747
1788fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1748fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1789 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1749 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);
1791 const ty = self.air.getRefType(ty_op.ty);1751 const ty = self.air.getRefType(ty_op.ty);
17921752
1793 if (!ty.hasCodeGenBits()) return WValue{ .none = {} };1753 if (!ty.hasCodeGenBits()) return WValue{ .none = {} };
17941754
1795 if (self.isByRef(ty)) {1755 if (isByRef(ty, self.target)) {
1796 const new_local = try self.allocStack(ty);1756 const new_local = try self.allocStack(ty);
1797 try self.store(new_local, operand, ty, 0);1757 try self.store(new_local, operand, ty, 0);
1798 return new_local;1758 return new_local;
1799 }1759 }
18001760
1801 return switch (operand) {1761 return self.load(operand, ty, 0);
1802 .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset),
1803 else => try self.load(operand, ty, 0),
1804 };
1805}1762}
18061763
1807fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {1764fn 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 {...@@ -1832,7 +1789,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
1832 };1789 };
18331790
1834 const opcode = buildOpcode(.{1791 const opcode = buildOpcode(.{
1835 .valtype1 = try self.typeToValtype(ty),1792 .valtype1 = typeToValtype(ty, self.target),
1836 .width = abi_size * 8, // use bitsize instead of byte size1793 .width = abi_size * 8, // use bitsize instead of byte size
1837 .op = .load,1794 .op = .load,
1838 .signedness = signedness,1795 .signedness = signedness,
...@@ -1859,11 +1816,11 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -1859,11 +1816,11 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1859 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };1816 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
18601817
1861 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1818 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1862 const lhs = self.resolveInst(bin_op.lhs);1819 const lhs = try self.resolveInst(bin_op.lhs);
1863 const rhs = self.resolveInst(bin_op.rhs);1820 const rhs = try self.resolveInst(bin_op.rhs);
1864 const operand_ty = self.air.typeOfIndex(inst);1821 const operand_ty = self.air.typeOfIndex(inst);
18651822
1866 if (self.isByRef(operand_ty)) {1823 if (isByRef(operand_ty, self.target)) {
1867 return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty});1824 return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty});
1868 }1825 }
18691826
...@@ -1873,7 +1830,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -1873,7 +1830,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1873 const bin_ty = self.air.typeOf(bin_op.lhs);1830 const bin_ty = self.air.typeOf(bin_op.lhs);
1874 const opcode: wasm.Opcode = buildOpcode(.{1831 const opcode: wasm.Opcode = buildOpcode(.{
1875 .op = op,1832 .op = op,
1876 .valtype1 = try self.typeToValtype(bin_ty),1833 .valtype1 = typeToValtype(bin_ty, self.target),
1877 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,1834 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
1878 });1835 });
1879 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));1836 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
...@@ -1886,8 +1843,8 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -1886,8 +1843,8 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
18861843
1887fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {1844fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1888 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1845 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1889 const lhs = self.resolveInst(bin_op.lhs);1846 const lhs = try self.resolveInst(bin_op.lhs);
1890 const rhs = self.resolveInst(bin_op.rhs);1847 const rhs = try self.resolveInst(bin_op.rhs);
18911848
1892 try self.emitWValue(lhs);1849 try self.emitWValue(lhs);
1893 try self.emitWValue(rhs);1850 try self.emitWValue(rhs);
...@@ -1895,7 +1852,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -1895,7 +1852,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1895 const bin_ty = self.air.typeOf(bin_op.lhs);1852 const bin_ty = self.air.typeOf(bin_op.lhs);
1896 const opcode: wasm.Opcode = buildOpcode(.{1853 const opcode: wasm.Opcode = buildOpcode(.{
1897 .op = op,1854 .op = op,
1898 .valtype1 = try self.typeToValtype(bin_ty),1855 .valtype1 = typeToValtype(bin_ty, self.target),
1899 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,1856 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
1900 });1857 });
1901 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));1858 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
...@@ -1933,7 +1890,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -1933,7 +1890,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1933 return bin_local;1890 return bin_local;
1934}1891}
19351892
1936fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {1893fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
1937 if (val.isUndefDeep()) return self.emitUndefined(ty);1894 if (val.isUndefDeep()) return self.emitUndefined(ty);
1938 switch (ty.zigTypeTag()) {1895 switch (ty.zigTypeTag()) {
1939 .Int => {1896 .Int => {
...@@ -1941,82 +1898,58 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {...@@ -1941,82 +1898,58 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
1941 // write constant1898 // write constant
1942 switch (int_info.signedness) {1899 switch (int_info.signedness) {
1943 .signed => switch (int_info.bits) {1900 .signed => switch (int_info.bits) {
1944 0...32 => return try self.addImm32(@intCast(i32, val.toSignedInt())),1901 0...32 => return WValue{ .imm32 = @bitCast(u32, @intCast(i32, val.toSignedInt())) },
1945 33...64 => return try self.addImm64(@bitCast(u64, val.toSignedInt())),1902 33...64 => return WValue{ .imm64 = @bitCast(u64, val.toSignedInt()) },
1946 65...128 => {},1903 else => unreachable,
1947 else => |bits| return self.fail("Wasm todo: emitConstant for integer with {d} bits", .{bits}),
1948 },1904 },
1949 .unsigned => switch (int_info.bits) {1905 .unsigned => switch (int_info.bits) {
1950 0...32 => return try self.addImm32(@bitCast(i32, @intCast(u32, val.toUnsignedInt()))),1906 0...32 => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
1951 33...64 => return try self.addImm64(val.toUnsignedInt()),1907 33...64 => return WValue{ .imm64 = val.toUnsignedInt() },
1952 65...128 => {},1908 else => unreachable,
1953 else => |bits| return self.fail("Wasm TODO: emitConstant for integer with {d} bits", .{bits}),
1954 },1909 },
1955 }1910 }
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);
1974 },1911 },
1975 .Bool => try self.addImm32(@intCast(i32, val.toSignedInt())),1912 .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
1976 .Float => {1913 .Float => switch (ty.floatBits(self.target)) {
1977 // write constant1914 0...32 => return WValue{ .float32 = val.toFloat(f32) },
1978 switch (ty.floatBits(self.target)) {1915 33...64 => return WValue{ .float64 = val.toFloat(f64) },
1979 0...32 => try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val.toFloat(f32) } }),1916 else => unreachable,
1980 64 => try self.addFloat64(val.toFloat(f64)),
1981 else => |bits| return self.fail("Wasm TODO: emitConstant for float with {d} bits", .{bits}),
1982 }
1983 },1917 },
1984 .Pointer => {1918 .Pointer => switch (val.tag()) {
1985 if (val.castTag(.slice)) |slice| {1919 .decl_ref => {
1986 var buf: Type.SlicePtrFieldTypeBuffer = undefined;1920 const decl = val.castTag(.decl_ref).?.data;
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;
1991 decl.markAlive();1921 decl.markAlive();
1992 // Function pointers use a table index, rather than a memory address1922 const target_sym_index = decl.link.wasm.sym_index;
1993 if (decl.ty.zigTypeTag() == .Fn) {1923 if (ty.isSlice()) {
1994 const target_sym_index = decl.link.wasm.sym_index;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) {
1995 try self.bin_file.addTableFunction(target_sym_index);1934 try self.bin_file.addTableFunction(target_sym_index);
1996 try self.addLabel(.function_index, target_sym_index);1935 return WValue{ .function_index = target_sym_index };
1997 } else {1936 } else return WValue{ .memory = target_sym_index };
1998 try self.addLabel(.memory_address, decl.link.wasm.sym_index);1937 },
1999 }1938 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
2000 } else if (val.castTag(.int_u64)) |int_ptr| {1939 .zero, .null_value => return WValue{ .imm32 = 0 },
2001 try self.addImm32(@bitCast(i32, @intCast(u32, int_ptr.data)));1940 else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}),
2002 } else if (val.tag() == .zero or val.tag() == .null_value) {1941 },
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 => {},
2009 .Enum => {1942 .Enum => {
2010 if (val.castTag(.enum_field_index)) |field_index| {1943 if (val.castTag(.enum_field_index)) |field_index| {
2011 switch (ty.tag()) {1944 switch (ty.tag()) {
2012 .enum_simple => try self.addImm32(@bitCast(i32, field_index.data)),1945 .enum_simple => return WValue{ .imm32 = field_index.data },
2013 .enum_full, .enum_nonexhaustive => {1946 .enum_full, .enum_nonexhaustive => {
2014 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;1947 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
2015 if (enum_full.values.count() != 0) {1948 if (enum_full.values.count() != 0) {
2016 const tag_val = enum_full.values.keys()[field_index.data];1949 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);
2018 } else {1951 } else {
2019 try self.addImm32(@bitCast(i32, field_index.data));1952 return WValue{ .imm32 = field_index.data };
2020 }1953 }
2021 },1954 },
2022 else => unreachable,1955 else => unreachable,
...@@ -2024,169 +1957,63 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {...@@ -2024,169 +1957,63 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
2024 } else {1957 } else {
2025 var int_tag_buffer: Type.Payload.Bits = undefined;1958 var int_tag_buffer: Type.Payload.Bits = undefined;
2026 const int_tag_ty = ty.intTagType(&int_tag_buffer);1959 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);
2028 }1961 }
2029 },1962 },
2030 .ErrorSet => {1963 .ErrorSet => switch (val.tag()) {
2031 const error_index = self.global_error_set.get(val.getError().?).?;1964 .@"error" => {
2032 try self.addImm32(@bitCast(i32, error_index));1965 const kv = try self.module.getErrorValue(val.getError().?);
1966 return WValue{ .imm32 = kv.value };
1967 },
1968 else => return WValue{ .imm32 = 0 },
2033 },1969 },
2034 .ErrorUnion => {1970 .ErrorUnion => {
2035 const error_type = ty.errorUnionSet();1971 const error_type = ty.errorUnionSet();
2036 const payload_type = ty.errorUnionPayload();1972 const is_pl = val.errorUnionIsPayload();
2037 if (val.castTag(.eu_payload)) |pl| {1973 const err_val = if (!is_pl) val else Value.initTag(.zero);
2038 const payload_val = pl.data;1974 return self.lowerConstant(err_val, error_type);
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 }
2055 },1975 },
2056 .Optional => {1976 .Optional => if (ty.isPtrLikeOptional()) {
2057 var buf: Type.Payload.ElemType = undefined;1977 var buf: Type.Payload.ElemType = undefined;
2058 const payload_type = ty.optionalChild(&buf);1978 return self.lowerConstant(val, ty.optionalChild(&buf));
2059 if (ty.isPtrLikeOptional()) {1979 } else {
2060 try self.emitConstant(val, payload_type);1980 const is_pl = val.tag() == .opt_payload;
2061 return;1981 return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 };
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);
2152 },1982 },
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}),
2154 }1984 }
2155}1985}
21561986
2157fn emitUndefined(self: *Self, ty: Type) InnerError!void {1987fn emitUndefined(self: *Self, ty: Type) InnerError!WValue {
2158 switch (ty.zigTypeTag()) {1988 switch (ty.zigTypeTag()) {
1989 .Bool, .ErrorSet => return WValue{ .imm32 = 0xaaaaaaaa },
2159 .Int => switch (ty.intInfo(self.target).bits) {1990 .Int => switch (ty.intInfo(self.target).bits) {
2160 0...32 => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))),1991 0...32 => return WValue{ .imm32 = 0xaaaaaaaa },
2161 33...64 => try self.addImm64(0xaaaaaaaaaaaaaaaa),1992 33...64 => return WValue{ .imm64 = 0xaaaaaaaaaaaaaaaa },
2162 else => |bits| return self.fail("Wasm TODO: emitUndefined for integer bitsize: {d}", .{bits}),1993 else => unreachable,
2163 },1994 },
2164 .Float => switch (ty.floatBits(self.target)) {1995 .Float => switch (ty.floatBits(self.target)) {
2165 0...32 => try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) } }),1996 0...32 => return WValue{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) },
2166 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))),1997 33...64 => return WValue{ .float64 = @bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa)) },
2167 else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}),1998 else => unreachable,
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);
2183 },1999 },
2184 .Pointer => switch (self.ptrSize()) {2000 .Pointer => switch (self.arch()) {
2185 4 => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))),2001 .wasm32 => return WValue{ .imm32 = 0xaaaaaaaa },
2186 8 => try self.addImm64(0xaaaaaaaaaaaaaaaa),2002 .wasm64 => return WValue{ .imm64 = 0xaaaaaaaaaaaaaaaa },
2187 else => unreachable,2003 else => unreachable,
2188 },2004 },
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()}),
2190 }2017 }
2191}2018}
21922019
...@@ -2219,8 +2046,8 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {...@@ -2219,8 +2046,8 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
2219 .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt())),2046 .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt())),
2220 },2047 },
2221 .ErrorSet => {2048 .ErrorSet => {
2222 const error_index = self.global_error_set.get(val.getError().?).?;2049 const kv = self.module.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function
2223 return @bitCast(i32, error_index);2050 return @bitCast(i32, kv.value);
2224 },2051 },
2225 else => unreachable, // Programmer called this function for an illegal type2052 else => unreachable, // Programmer called this function for an illegal type
2226 }2053 }
...@@ -2228,7 +2055,7 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {...@@ -2228,7 +2055,7 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
22282055
2229fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2056fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2230 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2057 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);
2232 const extra = self.air.extraData(Air.Block, ty_pl.payload);2059 const extra = self.air.extraData(Air.Block, ty_pl.payload);
2233 const body = self.air.extra[extra.end..][0..extra.data.body_len];2060 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 {...@@ -2285,7 +2112,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
22852112
2286fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2113fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2287 const pl_op = self.air.instructions.items(.data)[inst].pl_op;2114 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);
2289 const extra = self.air.extraData(Air.CondBr, pl_op.payload);2116 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
2290 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];2117 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
2291 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];2118 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 {...@@ -2312,8 +2139,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
23122139
2313fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue {2140fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue {
2314 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2141 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2315 const lhs = self.resolveInst(bin_op.lhs);2142 const lhs = try self.resolveInst(bin_op.lhs);
2316 const rhs = self.resolveInst(bin_op.rhs);2143 const rhs = try self.resolveInst(bin_op.rhs);
2317 const operand_ty = self.air.typeOf(bin_op.lhs);2144 const operand_ty = self.air.typeOf(bin_op.lhs);
23182145
2319 if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) {2146 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...@@ -2325,7 +2152,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
2325 // both lhs and rhs, as well as checking the payload are matching of lhs and rhs2152 // both lhs and rhs, as well as checking the payload are matching of lhs and rhs
2326 return self.cmpOptionals(lhs, rhs, operand_ty, op);2153 return self.cmpOptionals(lhs, rhs, operand_ty, op);
2327 }2154 }
2328 } else if (self.isByRef(operand_ty)) {2155 } else if (isByRef(operand_ty, self.target)) {
2329 return self.cmpBigInt(lhs, rhs, operand_ty, op);2156 return self.cmpBigInt(lhs, rhs, operand_ty, op);
2330 }2157 }
23312158
...@@ -2340,7 +2167,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner...@@ -2340,7 +2167,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
2340 break :blk operand_ty.intInfo(self.target).signedness;2167 break :blk operand_ty.intInfo(self.target).signedness;
2341 };2168 };
2342 const opcode: wasm.Opcode = buildOpcode(.{2169 const opcode: wasm.Opcode = buildOpcode(.{
2343 .valtype1 = try self.typeToValtype(operand_ty),2170 .valtype1 = typeToValtype(operand_ty, self.target),
2344 .op = switch (op) {2171 .op = switch (op) {
2345 .lt => .lt,2172 .lt => .lt,
2346 .lte => .le,2173 .lte => .le,
...@@ -2364,7 +2191,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2364,7 +2191,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
23642191
2365 // if operand has codegen bits we should break with a value2192 // if operand has codegen bits we should break with a value
2366 if (self.air.typeOf(br.operand).hasCodeGenBits()) {2193 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
2369 if (block.value != .none) {2196 if (block.value != .none) {
2370 try self.addLabel(.local_set, block.value.local);2197 try self.addLabel(.local_set, block.value.local);
...@@ -2382,7 +2209,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2382,7 +2209,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2382fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2209fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2383 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2210 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);
2386 try self.emitWValue(operand);2213 try self.emitWValue(operand);
23872214
2388 // wasm does not have booleans nor the `not` instruction, therefore compare with 02215 // 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 {...@@ -2412,20 +2239,14 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
24122239
2413fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2240fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2414 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2241 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2415 const operand = self.resolveInst(ty_op.operand);2242 const operand = try 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 }
2422 return operand;2243 return operand;
2423}2244}
24242245
2425fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2246fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2426 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2247 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2427 const extra = self.air.extraData(Air.StructField, ty_pl.payload);2248 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);
2429 const struct_ty = self.air.typeOf(extra.data.struct_operand).childType();2250 const struct_ty = self.air.typeOf(extra.data.struct_operand).childType();
2430 const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) catch {2251 const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) catch {
2431 return self.fail("Field type '{}' too big to fit into stack frame", .{2252 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 {...@@ -2437,7 +2258,7 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
24372258
2438fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue {2259fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue {
2439 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2260 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);
2441 const struct_ty = self.air.typeOf(ty_op.operand).childType();2262 const struct_ty = self.air.typeOf(ty_op.operand).childType();
2442 const field_ty = struct_ty.structFieldType(index);2263 const field_ty = struct_ty.structFieldType(index);
2443 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch {2264 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...@@ -2449,47 +2270,35 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr
2449}2270}
24502271
2451fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValue {2272fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValue {
2452 var final_offset = offset;2273 return self.buildPointerOffset(struct_ptr, offset, .new);
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);
2462}2274}
24632275
2464fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2276fn 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
2467 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2279 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2468 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;2280 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
2469 const struct_ty = self.air.typeOf(struct_field.struct_operand);2281 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);
2471 const field_index = struct_field.field_index;2283 const field_index = struct_field.field_index;
2472 const field_ty = struct_ty.structFieldType(field_index);2284 const field_ty = struct_ty.structFieldType(field_index);
2473 if (!field_ty.hasCodeGenBits()) return WValue.none;2285 if (!field_ty.hasCodeGenBits()) return WValue{ .none = {} };
2474 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch {2286 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch {
2475 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});2287 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
2476 };2288 };
24772289
2478 if (self.isByRef(field_ty)) {2290 if (isByRef(field_ty, self.target)) {
2479 return WValue{ .local_with_offset = .{ .local = operand.local, .offset = offset } };2291 return self.buildPointerOffset(operand, offset, .new);
2480 }2292 }
24812293
2482 switch (operand) {2294 return self.load(operand, field_ty, offset);
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 }
2486}2295}
24872296
2488fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2297fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2489 // result type is always 'noreturn'2298 // result type is always 'noreturn'
2490 const blocktype = wasm.block_empty;2299 const blocktype = wasm.block_empty;
2491 const pl_op = self.air.instructions.items(.data)[inst].pl_op;2300 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);
2493 const target_ty = self.air.typeOf(pl_op.operand);2302 const target_ty = self.air.typeOf(pl_op.operand);
2494 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);2303 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
2495 var extra_index: usize = switch_br.end;2304 var extra_index: usize = switch_br.end;
...@@ -2595,9 +2404,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2595,9 +2404,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2595 // for single value prong we can emit a simple if2404 // for single value prong we can emit a simple if
2596 if (case.values.len == 1) {2405 if (case.values.len == 1) {
2597 try self.emitWValue(target);2406 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);
2599 const opcode = buildOpcode(.{2409 const opcode = buildOpcode(.{
2600 .valtype1 = try self.typeToValtype(target_ty),2410 .valtype1 = typeToValtype(target_ty, self.target),
2601 .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition.2411 .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition.
2602 .signedness = signedness,2412 .signedness = signedness,
2603 });2413 });
...@@ -2608,9 +2418,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2608,9 +2418,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2608 try self.startBlock(.block, blocktype);2418 try self.startBlock(.block, blocktype);
2609 for (case.values) |value| {2419 for (case.values) |value| {
2610 try self.emitWValue(target);2420 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);
2612 const opcode = buildOpcode(.{2423 const opcode = buildOpcode(.{
2613 .valtype1 = try self.typeToValtype(target_ty),2424 .valtype1 = typeToValtype(target_ty, self.target),
2614 .op = .eq,2425 .op = .eq,
2615 .signedness = signedness,2426 .signedness = signedness,
2616 });2427 });
...@@ -2635,7 +2446,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2635,7 +2446,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26352446
2636fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {2447fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
2637 const un_op = self.air.instructions.items(.data)[inst].un_op;2448 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);
2639 const err_ty = self.air.typeOf(un_op);2450 const err_ty = self.air.typeOf(un_op);
2640 const pl_ty = err_ty.errorUnionPayload();2451 const pl_ty = err_ty.errorUnionPayload();
26412452
...@@ -2660,12 +2471,12 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W...@@ -2660,12 +2471,12 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
2660fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2471fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2661 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };2472 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2662 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2473 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);
2664 const err_ty = self.air.typeOf(ty_op.operand);2475 const err_ty = self.air.typeOf(ty_op.operand);
2665 const payload_ty = err_ty.errorUnionPayload();2476 const payload_ty = err_ty.errorUnionPayload();
2666 if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} };2477 if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} };
2667 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target));2478 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target));
2668 if (self.isByRef(payload_ty)) {2479 if (isByRef(payload_ty, self.target)) {
2669 return self.buildPointerOffset(operand, offset, .new);2480 return self.buildPointerOffset(operand, offset, .new);
2670 }2481 }
2671 return try self.load(operand, payload_ty, offset);2482 return try self.load(operand, payload_ty, offset);
...@@ -2675,7 +2486,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2675,7 +2486,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2675 if (self.liveness.isUnused(inst)) return WValue.none;2486 if (self.liveness.isUnused(inst)) return WValue.none;
26762487
2677 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2488 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);
2679 const err_ty = self.air.typeOf(ty_op.operand);2490 const err_ty = self.air.typeOf(ty_op.operand);
2680 const payload_ty = err_ty.errorUnionPayload();2491 const payload_ty = err_ty.errorUnionPayload();
2681 if (!payload_ty.hasCodeGenBits()) {2492 if (!payload_ty.hasCodeGenBits()) {
...@@ -2688,7 +2499,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2688,7 +2499,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2688fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2499fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2689 if (self.liveness.isUnused(inst)) return WValue.none;2500 if (self.liveness.isUnused(inst)) return WValue.none;
2690 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2501 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
2693 const op_ty = self.air.typeOf(ty_op.operand);2504 const op_ty = self.air.typeOf(ty_op.operand);
2694 if (!op_ty.hasCodeGenBits()) return operand;2505 if (!op_ty.hasCodeGenBits()) return operand;
...@@ -2710,7 +2521,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2710,7 +2521,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2710fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2521fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2711 if (self.liveness.isUnused(inst)) return WValue.none;2522 if (self.liveness.isUnused(inst)) return WValue.none;
2712 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2523 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);
2714 const err_ty = self.air.getRefType(ty_op.ty);2525 const err_ty = self.air.getRefType(ty_op.ty);
27152526
2716 const err_union = try self.allocStack(err_ty);2527 const err_union = try self.allocStack(err_ty);
...@@ -2724,7 +2535,7 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2724,7 +2535,7 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27242535
2725 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2536 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2726 const ty = self.air.getRefType(ty_op.ty);2537 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);
2728 const ref_ty = self.air.typeOf(ty_op.operand);2539 const ref_ty = self.air.typeOf(ty_op.operand);
2729 const ref_info = ref_ty.intInfo(self.target);2540 const ref_info = ref_ty.intInfo(self.target);
2730 const wanted_info = ty.intInfo(self.target);2541 const wanted_info = ty.intInfo(self.target);
...@@ -2755,7 +2566,7 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2755,7 +2566,7 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27552566
2756fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!WValue {2567fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!WValue {
2757 const un_op = self.air.instructions.items(.data)[inst].un_op;2568 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
2760 const op_ty = self.air.typeOf(un_op);2571 const op_ty = self.air.typeOf(un_op);
2761 const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty;2572 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)...@@ -2786,7 +2597,7 @@ fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode)
2786fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2597fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2787 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };2598 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2788 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2599 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);
2790 const opt_ty = self.air.typeOf(ty_op.operand);2601 const opt_ty = self.air.typeOf(ty_op.operand);
2791 const payload_ty = self.air.typeOfIndex(inst);2602 const payload_ty = self.air.typeOfIndex(inst);
2792 if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} };2603 if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} };
...@@ -2794,7 +2605,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2794,7 +2605,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27942605
2795 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);2606 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)) {
2798 return self.buildPointerOffset(operand, offset, .new);2609 return self.buildPointerOffset(operand, offset, .new);
2799 }2610 }
28002611
...@@ -2805,7 +2616,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2805,7 +2616,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2805 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };2616 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
28062617
2807 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2618 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);
2809 const opt_ty = self.air.typeOf(ty_op.operand).childType();2620 const opt_ty = self.air.typeOf(ty_op.operand).childType();
28102621
2811 var buf: Type.Payload.ElemType = undefined;2622 var buf: Type.Payload.ElemType = undefined;
...@@ -2820,7 +2631,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2820,7 +2631,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28202631
2821fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2632fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2822 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2633 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);
2824 const opt_ty = self.air.typeOf(ty_op.operand).childType();2635 const opt_ty = self.air.typeOf(ty_op.operand).childType();
2825 var buf: Type.Payload.ElemType = undefined;2636 var buf: Type.Payload.ElemType = undefined;
2826 const payload_ty = opt_ty.optionalChild(&buf);2637 const payload_ty = opt_ty.optionalChild(&buf);
...@@ -2856,7 +2667,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2856,7 +2667,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2856 return non_null_bit;2667 return non_null_bit;
2857 }2668 }
28582669
2859 const operand = self.resolveInst(ty_op.operand);2670 const operand = try self.resolveInst(ty_op.operand);
2860 const op_ty = self.air.typeOfIndex(inst);2671 const op_ty = self.air.typeOfIndex(inst);
2861 if (op_ty.isPtrLikeOptional()) {2672 if (op_ty.isPtrLikeOptional()) {
2862 return operand;2673 return operand;
...@@ -2882,8 +2693,8 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2882,8 +2693,8 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28822693
2883 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2694 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2884 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;2695 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
2885 const lhs = self.resolveInst(bin_op.lhs);2696 const lhs = try self.resolveInst(bin_op.lhs);
2886 const rhs = self.resolveInst(bin_op.rhs);2697 const rhs = try self.resolveInst(bin_op.rhs);
2887 const slice_ty = self.air.typeOfIndex(inst);2698 const slice_ty = self.air.typeOfIndex(inst);
28882699
2889 const slice = try self.allocStack(slice_ty);2700 const slice = try self.allocStack(slice_ty);
...@@ -2897,7 +2708,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2897,7 +2708,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2897 if (self.liveness.isUnused(inst)) return WValue.none;2708 if (self.liveness.isUnused(inst)) return WValue.none;
28982709
2899 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2710 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
2902 return try self.load(operand, Type.usize, self.ptrSize());2713 return try self.load(operand, Type.usize, self.ptrSize());
2903}2714}
...@@ -2907,8 +2718,8 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2907,8 +2718,8 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29072718
2908 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2719 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2909 const slice_ty = self.air.typeOf(bin_op.lhs);2720 const slice_ty = self.air.typeOf(bin_op.lhs);
2910 const slice = self.resolveInst(bin_op.lhs);2721 const slice = try self.resolveInst(bin_op.lhs);
2911 const index = self.resolveInst(bin_op.rhs);2722 const index = try self.resolveInst(bin_op.rhs);
2912 const elem_ty = slice_ty.childType();2723 const elem_ty = slice_ty.childType();
2913 const elem_size = elem_ty.abiSize(self.target);2724 const elem_size = elem_ty.abiSize(self.target);
29142725
...@@ -2925,7 +2736,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2925,7 +2736,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2925 const result = try self.allocLocal(elem_ty);2736 const result = try self.allocLocal(elem_ty);
2926 try self.addLabel(.local_set, result.local);2737 try self.addLabel(.local_set, result.local);
29272738
2928 if (self.isByRef(elem_ty)) {2739 if (isByRef(elem_ty, self.target)) {
2929 return result;2740 return result;
2930 }2741 }
2931 return try self.load(result, elem_ty, 0);2742 return try self.load(result, elem_ty, 0);
...@@ -2939,8 +2750,8 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2939,8 +2750,8 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2939 const elem_ty = self.air.getRefType(ty_pl.ty).childType();2750 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
2940 const elem_size = elem_ty.abiSize(self.target);2751 const elem_size = elem_ty.abiSize(self.target);
29412752
2942 const slice = self.resolveInst(bin_op.lhs);2753 const slice = try self.resolveInst(bin_op.lhs);
2943 const index = self.resolveInst(bin_op.rhs);2754 const index = try self.resolveInst(bin_op.rhs);
29442755
2945 const slice_ptr = try self.load(slice, slice_ty, 0);2756 const slice_ptr = try self.load(slice, slice_ty, 0);
2946 try self.addLabel(.local_get, slice_ptr.local);2757 try self.addLabel(.local_get, slice_ptr.local);
...@@ -2959,14 +2770,14 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2959,14 +2770,14 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2959fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2770fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2960 if (self.liveness.isUnused(inst)) return WValue.none;2771 if (self.liveness.isUnused(inst)) return WValue.none;
2961 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2772 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);
2963 return try self.load(operand, Type.usize, 0);2774 return try self.load(operand, Type.usize, 0);
2964}2775}
29652776
2966fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2777fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2967 if (self.liveness.isUnused(inst)) return WValue.none;2778 if (self.liveness.isUnused(inst)) return WValue.none;
2968 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2779 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);
2970 const op_ty = self.air.typeOf(ty_op.operand);2781 const op_ty = self.air.typeOf(ty_op.operand);
2971 const int_info = self.air.getRefType(ty_op.ty).intInfo(self.target);2782 const int_info = self.air.getRefType(ty_op.ty).intInfo(self.target);
2972 const wanted_bits = int_info.bits;2783 const wanted_bits = int_info.bits;
...@@ -3025,12 +2836,12 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3025,12 +2836,12 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30252836
3026fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2837fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3027 const un_op = self.air.instructions.items(.data)[inst].un_op;2838 const un_op = self.air.instructions.items(.data)[inst].un_op;
3028 return self.resolveInst(un_op);2839 return try self.resolveInst(un_op);
3029}2840}
30302841
3031fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2842fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3032 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2843 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);
3034 const array_ty = self.air.typeOf(ty_op.operand).childType();2845 const array_ty = self.air.typeOf(ty_op.operand).childType();
3035 const ty = Type.@"usize";2846 const ty = Type.@"usize";
3036 const ptr_width = @intCast(u32, ty.abiSize(self.target));2847 const ptr_width = @intCast(u32, ty.abiSize(self.target));
...@@ -3057,7 +2868,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3057,7 +2868,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3057fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2868fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3058 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };2869 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3059 const un_op = self.air.instructions.items(.data)[inst].un_op;2870 const un_op = self.air.instructions.items(.data)[inst].un_op;
3060 return self.resolveInst(un_op);2871 return try self.resolveInst(un_op);
3061}2872}
30622873
3063fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2874fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -3065,8 +2876,8 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3065,8 +2876,8 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30652876
3066 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2877 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3067 const ptr_ty = self.air.typeOf(bin_op.lhs);2878 const ptr_ty = self.air.typeOf(bin_op.lhs);
3068 const pointer = self.resolveInst(bin_op.lhs);2879 const pointer = try self.resolveInst(bin_op.lhs);
3069 const index = self.resolveInst(bin_op.rhs);2880 const index = try self.resolveInst(bin_op.rhs);
3070 const elem_ty = ptr_ty.childType();2881 const elem_ty = ptr_ty.childType();
3071 const elem_size = elem_ty.abiSize(self.target);2882 const elem_size = elem_ty.abiSize(self.target);
30722883
...@@ -3086,7 +2897,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3086,7 +2897,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30862897
3087 const result = try self.allocLocal(elem_ty);2898 const result = try self.allocLocal(elem_ty);
3088 try self.addLabel(.local_set, result.local);2899 try self.addLabel(.local_set, result.local);
3089 if (self.isByRef(elem_ty)) {2900 if (isByRef(elem_ty, self.target)) {
3090 return result;2901 return result;
3091 }2902 }
3092 return try self.load(result, elem_ty, 0);2903 return try self.load(result, elem_ty, 0);
...@@ -3100,8 +2911,8 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3100,8 +2911,8 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3100 const elem_ty = self.air.getRefType(ty_pl.ty).childType();2911 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
3101 const elem_size = elem_ty.abiSize(self.target);2912 const elem_size = elem_ty.abiSize(self.target);
31022913
3103 const ptr = self.resolveInst(bin_op.lhs);2914 const ptr = try self.resolveInst(bin_op.lhs);
3104 const index = self.resolveInst(bin_op.rhs);2915 const index = try self.resolveInst(bin_op.rhs);
31052916
3106 // load pointer onto the stack2917 // load pointer onto the stack
3107 if (ptr_ty.isSlice()) {2918 if (ptr_ty.isSlice()) {
...@@ -3125,15 +2936,15 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3125,15 +2936,15 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3125fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {2936fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
3126 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };2937 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3127 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2938 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3128 const ptr = self.resolveInst(bin_op.lhs);2939 const ptr = try self.resolveInst(bin_op.lhs);
3129 const offset = self.resolveInst(bin_op.rhs);2940 const offset = try self.resolveInst(bin_op.rhs);
3130 const ptr_ty = self.air.typeOf(bin_op.lhs);2941 const ptr_ty = self.air.typeOf(bin_op.lhs);
3131 const pointee_ty = switch (ptr_ty.ptrSize()) {2942 const pointee_ty = switch (ptr_ty.ptrSize()) {
3132 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type2943 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
3133 else => ptr_ty.childType(),2944 else => ptr_ty.childType(),
3134 };2945 };
31352946
3136 const valtype = try self.typeToValtype(Type.usize);2947 const valtype = typeToValtype(Type.usize, self.target);
3137 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });2948 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });
3138 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });2949 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });
31392950
...@@ -3152,9 +2963,9 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3152,9 +2963,9 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3152 const pl_op = self.air.instructions.items(.data)[inst].pl_op;2963 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3153 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;2964 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;
31542965
3155 const ptr = self.resolveInst(pl_op.operand);2966 const ptr = try self.resolveInst(pl_op.operand);
3156 const value = self.resolveInst(bin_op.lhs);2967 const value = try self.resolveInst(bin_op.lhs);
3157 const len = self.resolveInst(bin_op.rhs);2968 const len = try self.resolveInst(bin_op.rhs);
3158 try self.memSet(ptr, len, value);2969 try self.memSet(ptr, len, value);
31592970
3160 return WValue.none;2971 return WValue.none;
...@@ -3221,8 +3032,8 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3221,8 +3032,8 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
32213032
3222 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3033 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3223 const array_ty = self.air.typeOf(bin_op.lhs);3034 const array_ty = self.air.typeOf(bin_op.lhs);
3224 const array = self.resolveInst(bin_op.lhs);3035 const array = try self.resolveInst(bin_op.lhs);
3225 const index = self.resolveInst(bin_op.rhs);3036 const index = try self.resolveInst(bin_op.rhs);
3226 const elem_ty = array_ty.childType();3037 const elem_ty = array_ty.childType();
3227 const elem_size = elem_ty.abiSize(self.target);3038 const elem_size = elem_ty.abiSize(self.target);
32283039
...@@ -3236,7 +3047,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3236,7 +3047,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3236 const result = try self.allocLocal(elem_ty);3047 const result = try self.allocLocal(elem_ty);
3237 try self.addLabel(.local_set, result.local);3048 try self.addLabel(.local_set, result.local);
32383049
3239 if (self.isByRef(elem_ty)) {3050 if (isByRef(elem_ty, self.target)) {
3240 return result;3051 return result;
3241 }3052 }
3242 return try self.load(result, elem_ty, 0);3053 return try self.load(result, elem_ty, 0);
...@@ -3246,15 +3057,15 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3246,15 +3057,15 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3246 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };3057 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
32473058
3248 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3059 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);
3250 const dest_ty = self.air.typeOfIndex(inst);3061 const dest_ty = self.air.typeOfIndex(inst);
3251 const op_ty = self.air.typeOf(ty_op.operand);3062 const op_ty = self.air.typeOf(ty_op.operand);
32523063
3253 try self.emitWValue(operand);3064 try self.emitWValue(operand);
3254 const op = buildOpcode(.{3065 const op = buildOpcode(.{
3255 .op = .trunc,3066 .op = .trunc,
3256 .valtype1 = try self.typeToValtype(dest_ty),3067 .valtype1 = typeToValtype(dest_ty, self.target),
3257 .valtype2 = try self.typeToValtype(op_ty),3068 .valtype2 = typeToValtype(op_ty, self.target),
3258 .signedness = if (dest_ty.isSignedInt()) .signed else .unsigned,3069 .signedness = if (dest_ty.isSignedInt()) .signed else .unsigned,
3259 });3070 });
3260 try self.addTag(Mir.Inst.Tag.fromOpcode(op));3071 try self.addTag(Mir.Inst.Tag.fromOpcode(op));
...@@ -3268,7 +3079,7 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3268,7 +3079,7 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3268 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };3079 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
32693080
3270 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3081 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
3273 _ = ty_op;3084 _ = ty_op;
3274 _ = operand;3085 _ = operand;
...@@ -3318,7 +3129,7 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std...@@ -3318,7 +3129,7 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
33183129
3319 try self.emitWValue(lhs_pl);3130 try self.emitWValue(lhs_pl);
3320 try self.emitWValue(rhs_pl);3131 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) });
3322 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));3133 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3323 try self.addLabel(.br_if, 0);3134 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 {...@@ -323,16 +323,24 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
323323
324fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {324fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
325 const symbol_index = emit.mir.instructions.items(.data)[inst].label;325 const symbol_index = emit.mir.instructions.items(.data)[inst].label;
326 try emit.code.append(std.wasm.opcode(.i32_const));326 const mem_offset = emit.offset() + 1;
327 const mem_offset = emit.offset();327 const is_wasm32 = emit.bin_file.options.target.cpu.arch == .wasm32;
328 var buf: [5]u8 = undefined;328 if (is_wasm32) {
329 leb128.writeUnsignedFixed(5, &buf, symbol_index);329 try emit.code.append(std.wasm.opcode(.i32_const));
330 try emit.code.appendSlice(&buf);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
332 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{340 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
333 .offset = mem_offset,341 .offset = mem_offset,
334 .index = symbol_index,342 .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,
336 });344 });
337}345}
338346
src/link/Wasm.zig+111-34
...@@ -19,7 +19,7 @@ const trace = @import("../tracy.zig").trace;...@@ -19,7 +19,7 @@ const trace = @import("../tracy.zig").trace;
19const build_options = @import("build_options");19const build_options = @import("build_options");
20const wasi_libc = @import("../wasi_libc.zig");20const wasi_libc = @import("../wasi_libc.zig");
21const Cache = @import("../Cache.zig");21const Cache = @import("../Cache.zig");
22const TypedValue = @import("../TypedValue.zig");22const Type = @import("../type.zig").Type;
23const LlvmObject = @import("../codegen/llvm.zig").Object;23const LlvmObject = @import("../codegen/llvm.zig").Object;
24const Air = @import("../Air.zig");24const Air = @import("../Air.zig");
25const Liveness = @import("../Liveness.zig");25const Liveness = @import("../Liveness.zig");
...@@ -234,12 +234,12 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live...@@ -234,12 +234,12 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
234 .locals = .{},234 .locals = .{},
235 .target = self.base.options.target,235 .target = self.base.options.target,
236 .bin_file = self,236 .bin_file = self,
237 .global_error_set = self.base.options.module.?.global_error_set,237 .module = module,
238 };238 };
239 defer codegen.deinit();239 defer codegen.deinit();
240240
241 // generate the 'code' section for the function declaration241 // generate the 'code' section for the function declaration
242 const result = codegen.genFunc() catch |err| switch (err) {242 codegen.genFunc() catch |err| switch (err) {
243 error.CodegenFail => {243 error.CodegenFail => {
244 decl.analysis = .codegen_failure;244 decl.analysis = .codegen_failure;
245 try module.failed_decls.put(module.gpa, decl, codegen.err_msg);245 try module.failed_decls.put(module.gpa, decl, codegen.err_msg);
...@@ -247,7 +247,7 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live...@@ -247,7 +247,7 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
247 },247 },
248 else => |e| return e,248 else => |e| return e,
249 };249 };
250 return self.finishUpdateDecl(decl, result, &codegen);250 return self.finishUpdateDecl(decl, codegen.code.items);
251}251}
252252
253// Generate code for the Decl, storing it in memory to be later written to253// Generate code for the Decl, storing it in memory to be later written to
...@@ -264,40 +264,37 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {...@@ -264,40 +264,37 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
264264
265 decl.link.wasm.clear();265 decl.link.wasm.clear();
266266
267 var codegen: CodeGen = .{267 var code_writer = std.ArrayList(u8).init(self.base.allocator);
268 defer code_writer.deinit();
269 var decl_gen: CodeGen.DeclGen = .{
268 .gpa = self.base.allocator,270 .gpa = self.base.allocator,
269 .air = undefined,
270 .liveness = undefined,
271 .values = .{},
272 .code = std.ArrayList(u8).init(self.base.allocator),
273 .decl = decl,271 .decl = decl,
274 .err_msg = undefined,272 .symbol_index = decl.link.wasm.sym_index,
275 .locals = .{},
276 .target = self.base.options.target,
277 .bin_file = self,273 .bin_file = self,
278 .global_error_set = self.base.options.module.?.global_error_set,274 .err_msg = undefined,
275 .code = &code_writer,
276 .module = module,
279 };277 };
280 defer codegen.deinit();
281278
282 // generate the 'code' section for the function declaration279 // generate the 'code' section for the function declaration
283 const result = codegen.genDecl() catch |err| switch (err) {280 const result = decl_gen.genDecl() catch |err| switch (err) {
284 error.CodegenFail => {281 error.CodegenFail => {
285 decl.analysis = .codegen_failure;282 decl.analysis = .codegen_failure;
286 try module.failed_decls.put(module.gpa, decl, codegen.err_msg);283 try module.failed_decls.put(module.gpa, decl, decl_gen.err_msg);
287 return;284 return;
288 },285 },
289 else => |e| return e,286 else => |e| return e,
290 };287 };
291288
292 return self.finishUpdateDecl(decl, result, &codegen);289 const code = switch (result) {
293}290 .externally_managed => |data| data,
294291 .appended => code_writer.items,
295fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, result: CodeGen.Result, codegen: *CodeGen) !void {
296 const code: []const u8 = switch (result) {
297 .appended => @as([]const u8, codegen.code.items),
298 .externally_managed => |payload| payload,
299 };292 };
300293
294 return self.finishUpdateDecl(decl, code);
295}
296
297fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
301 if (decl.isExtern()) {298 if (decl.isExtern()) {
302 try self.addOrUpdateImport(decl);299 try self.addOrUpdateImport(decl);
303 return;300 return;
...@@ -306,9 +303,75 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, result: CodeGen.Result, cod...@@ -306,9 +303,75 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, result: CodeGen.Result, cod
306 if (code.len == 0) return;303 if (code.len == 0) return;
307 const atom: *Atom = &decl.link.wasm;304 const atom: *Atom = &decl.link.wasm;
308 atom.size = @intCast(u32, code.len);305 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;
309 try atom.code.appendSlice(self.base.allocator, code);308 try atom.code.appendSlice(self.base.allocator, code);
310}309}
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
312pub fn updateDeclExports(375pub fn updateDeclExports(
313 self: *Wasm,376 self: *Wasm,
314 module: *Module,377 module: *Module,
...@@ -329,9 +392,12 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {...@@ -329,9 +392,12 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
329 }392 }
330 const atom = &decl.link.wasm;393 const atom = &decl.link.wasm;
331 self.symbols_free_list.append(self.base.allocator, atom.sym_index) catch {};394 self.symbols_free_list.append(self.base.allocator, atom.sym_index) catch {};
332 atom.deinit(self.base.allocator);
333 _ = self.decls.remove(decl);395 _ = self.decls.remove(decl);
334 self.symbols.items[atom.sym_index].tag = .dead; // to ensure it does not end in the names section396 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
336 if (decl.isExtern()) {402 if (decl.isExtern()) {
337 const import = self.imports.fetchRemove(decl.link.wasm.sym_index).?.value;403 const import = self.imports.fetchRemove(decl.link.wasm.sym_index).?.value;
...@@ -377,14 +443,16 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {...@@ -377,14 +443,16 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
377 }443 }
378}444}
379445
380fn parseDeclIntoAtom(self: *Wasm, decl: *Module.Decl) !void {446const Kind = union(enum) {
381 const atom: *Atom = &decl.link.wasm;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 {
382 const symbol: *Symbol = &self.symbols.items[atom.sym_index];453 const symbol: *Symbol = &self.symbols.items[atom.sym_index];
383 symbol.name = decl.name;454 const final_index: u32 = switch (kind) {
384 atom.alignment = decl.ty.abiAlignment(self.base.options.target);455 .function => |fn_data| result: {
385 const final_index: u32 = switch (decl.ty.zigTypeTag()) {
386 .Fn => result: {
387 const fn_data = decl.fn_link.wasm;
388 const type_index = fn_data.type_index;456 const type_index = fn_data.type_index;
389 const index = @intCast(u32, self.functions.items.len + self.imported_functions_count);457 const index = @intCast(u32, self.functions.items.len + self.imported_functions_count);
390 try self.functions.append(self.base.allocator, .{ .type_index = type_index });458 try self.functions.append(self.base.allocator, .{ .type_index = type_index });
...@@ -402,7 +470,7 @@ fn parseDeclIntoAtom(self: *Wasm, decl: *Module.Decl) !void {...@@ -402,7 +470,7 @@ fn parseDeclIntoAtom(self: *Wasm, decl: *Module.Decl) !void {
402470
403 break :result self.code_section_index.?;471 break :result self.code_section_index.?;
404 },472 },
405 else => result: {473 .data => result: {
406 const gop = try self.data_segments.getOrPut(self.base.allocator, ".rodata");474 const gop = try self.data_segments.getOrPut(self.base.allocator, ".rodata");
407 const atom_index = if (gop.found_existing) blk: {475 const atom_index = if (gop.found_existing) blk: {
408 self.segments.items[gop.value_ptr.*].size += atom.size;476 self.segments.items[gop.value_ptr.*].size += atom.size;
...@@ -430,7 +498,6 @@ fn parseDeclIntoAtom(self: *Wasm, decl: *Module.Decl) !void {...@@ -430,7 +498,6 @@ fn parseDeclIntoAtom(self: *Wasm, decl: *Module.Decl) !void {
430 });498 });
431 symbol.tag = .data;499 symbol.tag = .data;
432 symbol.index = info_index;500 symbol.index = info_index;
433 atom.alignment = decl.ty.abiAlignment(self.base.options.target);
434501
435 break :result atom_index;502 break :result atom_index;
436 },503 },
...@@ -617,7 +684,17 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -617,7 +684,17 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
617 var decl_it = self.decls.keyIterator();684 var decl_it = self.decls.keyIterator();
618 while (decl_it.next()) |decl| {685 while (decl_it.next()) |decl| {
619 if (decl.*.isExtern()) continue;686 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 }
621 }698 }
622699
623 try self.setupMemory();700 try self.setupMemory();
src/link/Wasm/Atom.zig+18
...@@ -31,6 +31,10 @@ next: ?*Atom,...@@ -31,6 +31,10 @@ next: ?*Atom,
31/// is null when this atom is the first in its order31/// is null when this atom is the first in its order
32prev: ?*Atom,32prev: ?*Atom,
3333
34/// Contains atoms local to a decl, all managed by this `Atom`.
35/// When the parent atom is being freed, it will also do so for all local atoms.
36locals: std.ArrayListUnmanaged(Atom) = .{},
37
34/// Represents a default empty wasm `Atom`38/// Represents a default empty wasm `Atom`
35pub const empty: Atom = .{39pub const empty: Atom = .{
36 .alignment = 0,40 .alignment = 0,
...@@ -45,6 +49,11 @@ pub const empty: Atom = .{...@@ -45,6 +49,11 @@ pub const empty: Atom = .{
45pub fn deinit(self: *Atom, gpa: Allocator) void {49pub fn deinit(self: *Atom, gpa: Allocator) void {
46 self.relocs.deinit(gpa);50 self.relocs.deinit(gpa);
47 self.code.deinit(gpa);51 self.code.deinit(gpa);
52
53 while (self.locals.popOrNull()) |*local| {
54 local.deinit(gpa);
55 }
56 self.locals.deinit(gpa);
48}57}
4958
50/// Sets the length of relocations and code to '0',59/// Sets the length of relocations and code to '0',
...@@ -72,6 +81,15 @@ pub fn getFirst(self: *Atom) *Atom {...@@ -72,6 +81,15 @@ pub fn getFirst(self: *Atom) *Atom {
72 return tmp;81 return tmp;
73}82}
7483
84/// Returns the atom for the given `symbol_index`.
85/// This can be either the `Atom` itself, or one of its locals.
86pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {
87 if (self.sym_index == symbol_index) return self;
88 return for (self.locals.items) |*local_atom| {
89 if (local_atom.sym_index == symbol_index) break local_atom;
90 } else unreachable; // Used a symbol index not present in this atom or its children.
91}
92
75/// Resolves the relocations within the atom, writing the new value93/// Resolves the relocations within the atom, writing the new value
76/// at the calculated offset.94/// at the calculated offset.
77pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {95pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
test/behavior/array.zig-7
...@@ -146,13 +146,6 @@ test "void arrays" {...@@ -146,13 +146,6 @@ test "void arrays" {
146test "nested arrays" {146test "nested arrays" {
147 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;147 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
156 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };149 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
157 for (array_of_strings) |s, i| {150 for (array_of_strings) |s, i| {
158 if (i == 0) try expect(mem.eql(u8, s, "hello"));151 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)" {...@@ -62,12 +62,6 @@ test "ignore lval with underscore (for loop)" {
62}62}
6363
64test "basic for loop" {64test "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 }
71 const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3;65 const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3;
7266
73 var buffer: [expected_result.len]u8 = undefined;67 var buffer: [expected_result.len]u8 = undefined;