| ... | @@ -28,16 +28,15 @@ const WValue = union(enum) { | ... | @@ -28,16 +28,15 @@ const WValue = union(enum) { |
| 28 | local: u32, | 28 | local: u32, |
| 29 | /// Holds a memoized typed value | 29 | /// Holds a memoized typed value |
| 30 | constant: TypedValue, | 30 | constant: TypedValue, |
| 31 | /// Offset position in the list of MIR instructions | 31 | /// Used for types that contains of multiple areas within |
| 32 | mir_offset: usize, | 32 | /// a memory region in the stack. |
| 33 | /// Used for variables that create multiple locals on the stack when allocated | 33 | /// The local represents the position in the stack, |
| 34 | /// such as structs and optionals. | 34 | /// whereas the offset represents the offset from that position. |
| 35 | multi_value: struct { | 35 | local_with_offset: struct { |
| 36 | /// The index of the first local variable | 36 | /// Index of the local variable |
| 37 | index: u32, | 37 | local: u32, |
| 38 | /// The count of local variables this `WValue` consists of. | 38 | /// The offset from the local's stack position |
| 39 | /// i.e. an ErrorUnion has a 'count' of 2. | 39 | offset: u32, |
| 40 | count: u32, | | |
| 41 | }, | 40 | }, |
| 42 | }; | 41 | }; |
| 43 | | 42 | |
| ... | @@ -187,7 +186,14 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -187,7 +186,14 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 187 | }, | 186 | }, |
| 188 | 32 => switch (args.valtype1.?) { | 187 | 32 => switch (args.valtype1.?) { |
| 189 | .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u, | 188 | .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u, |
| 190 | .i32, .f32, .f64 => unreachable, | 189 | .i32 => return .i32_load, |
| | 190 | .f32 => return .f32_load, |
| | 191 | .f64 => unreachable, |
| | 192 | }, |
| | 193 | 64 => switch (args.valtype1.?) { |
| | 194 | .i64 => return .i64_load, |
| | 195 | .f64 => return .f64_load, |
| | 196 | else => unreachable, |
| 191 | }, | 197 | }, |
| 192 | else => unreachable, | 198 | else => unreachable, |
| 193 | } else switch (args.valtype1.?) { | 199 | } else switch (args.valtype1.?) { |
| ... | @@ -210,7 +216,14 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -210,7 +216,14 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 210 | }, | 216 | }, |
| 211 | 32 => switch (args.valtype1.?) { | 217 | 32 => switch (args.valtype1.?) { |
| 212 | .i64 => return .i64_store32, | 218 | .i64 => return .i64_store32, |
| 213 | .i32, .f32, .f64 => unreachable, | 219 | .i32 => return .i32_store, |
| | 220 | .f32 => return .f32_store, |
| | 221 | .f64 => unreachable, |
| | 222 | }, |
| | 223 | 64 => switch (args.valtype1.?) { |
| | 224 | .i64 => return .i64_store, |
| | 225 | .f64 => return .f64_store, |
| | 226 | else => unreachable, |
| 214 | }, | 227 | }, |
| 215 | else => unreachable, | 228 | else => unreachable, |
| 216 | } | 229 | } |
| ... | @@ -499,7 +512,10 @@ gpa: *mem.Allocator, | ... | @@ -499,7 +512,10 @@ gpa: *mem.Allocator, |
| 499 | /// Table to save `WValue`'s generated by an `Air.Inst` | 512 | /// Table to save `WValue`'s generated by an `Air.Inst` |
| 500 | values: ValueTable, | 513 | values: ValueTable, |
| 501 | /// Mapping from Air.Inst.Index to block ids | 514 | /// Mapping from Air.Inst.Index to block ids |
| 502 | blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{}, | 515 | blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct { |
| | 516 | label: u32, |
| | 517 | value: WValue, |
| | 518 | }) = .{}, |
| 503 | /// `bytes` contains the wasm bytecode belonging to the 'code' section. | 519 | /// `bytes` contains the wasm bytecode belonging to the 'code' section. |
| 504 | code: ArrayList(u8), | 520 | code: ArrayList(u8), |
| 505 | /// Contains the generated function type bytecode for the current function | 521 | /// Contains the generated function type bytecode for the current function |
| ... | @@ -509,6 +525,9 @@ func_type_data: ArrayList(u8), | ... | @@ -509,6 +525,9 @@ func_type_data: ArrayList(u8), |
| 509 | /// NOTE: arguments share the index with locals therefore the first variable | 525 | /// NOTE: arguments share the index with locals therefore the first variable |
| 510 | /// will have the index that comes after the last argument's index | 526 | /// will have the index that comes after the last argument's index |
| 511 | local_index: u32 = 0, | 527 | local_index: u32 = 0, |
| | 528 | /// The index of the current argument. |
| | 529 | /// Used to track which argument is being referenced in `airArg`. |
| | 530 | arg_index: u32 = 0, |
| 512 | /// If codegen fails, an error messages will be allocated and saved in `err_msg` | 531 | /// If codegen fails, an error messages will be allocated and saved in `err_msg` |
| 513 | err_msg: *Module.ErrorMsg, | 532 | err_msg: *Module.ErrorMsg, |
| 514 | /// Current block depth. Used to calculate the relative difference between a break | 533 | /// Current block depth. Used to calculate the relative difference between a break |
| ... | @@ -529,6 +548,16 @@ global_error_set: std.StringHashMapUnmanaged(Module.ErrorInt), | ... | @@ -529,6 +548,16 @@ global_error_set: std.StringHashMapUnmanaged(Module.ErrorInt), |
| 529 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | 548 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| 530 | /// Contains extra data for MIR | 549 | /// Contains extra data for MIR |
| 531 | mir_extra: std.ArrayListUnmanaged(u32) = .{}, | 550 | mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| | 551 | /// When a function is executing, we store the the current stack pointer's value within this local. |
| | 552 | /// This value is then used to restore the stack pointer to the original value at the return of the function. |
| | 553 | initial_stack_value: WValue = .none, |
| | 554 | /// Arguments of this function declaration |
| | 555 | /// This will be set after `resolveCallingConventionValues` |
| | 556 | args: []WValue = undefined, |
| | 557 | /// This will only be `.none` if the function returns void, or returns an immediate. |
| | 558 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated |
| | 559 | /// before this function returns its execution to the caller. |
| | 560 | return_value: WValue = .none, |
| 532 | | 561 | |
| 533 | const InnerError = error{ | 562 | const InnerError = error{ |
| 534 | OutOfMemory, | 563 | OutOfMemory, |
| ... | @@ -662,9 +691,11 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { | ... | @@ -662,9 +691,11 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { |
| 662 | .Bool, | 691 | .Bool, |
| 663 | .Pointer, | 692 | .Pointer, |
| 664 | .ErrorSet, | 693 | .ErrorSet, |
| | 694 | .Struct, |
| | 695 | .ErrorUnion, |
| | 696 | .Optional, |
| 665 | => wasm.Valtype.i32, | 697 | => wasm.Valtype.i32, |
| 666 | .Struct, .ErrorUnion, .Optional => unreachable, // Multi typed, must be handled individually. | 698 | else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}), |
| 667 | else => |tag| self.fail("TODO - Wasm valtype for type '{s}'", .{tag}), | | |
| 668 | }; | 699 | }; |
| 669 | } | 700 | } |
| 670 | | 701 | |
| ... | @@ -686,78 +717,21 @@ fn genBlockType(self: *Self, ty: Type) InnerError!u8 { | ... | @@ -686,78 +717,21 @@ fn genBlockType(self: *Self, ty: Type) InnerError!u8 { |
| 686 | /// Writes the bytecode depending on the given `WValue` in `val` | 717 | /// Writes the bytecode depending on the given `WValue` in `val` |
| 687 | fn emitWValue(self: *Self, val: WValue) InnerError!void { | 718 | fn emitWValue(self: *Self, val: WValue) InnerError!void { |
| 688 | switch (val) { | 719 | switch (val) { |
| 689 | .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually | 720 | .none => {}, // no-op |
| 690 | .none, .mir_offset => {}, // no-op | 721 | .local_with_offset => |with_off| try self.addLabel(.local_get, with_off.local), |
| 691 | .local => |idx| { | 722 | .local => |idx| try self.addLabel(.local_get, idx), |
| 692 | try self.addLabel(.local_get, idx); | | |
| 693 | }, | | |
| 694 | .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack | 723 | .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack |
| 695 | } | 724 | } |
| 696 | } | 725 | } |
| 697 | | 726 | |
| 698 | /// Creates one or multiple locals for a given `Type`. | 727 | /// Creates one locals for a given `Type`. |
| 699 | /// Returns a corresponding `Wvalue` that can either be of tag | 728 | /// Returns a corresponding `Wvalue` with `local` as active tag |
| 700 | /// local or multi_value | | |
| 701 | fn allocLocal(self: *Self, ty: Type) InnerError!WValue { | 729 | fn allocLocal(self: *Self, ty: Type) InnerError!WValue { |
| 702 | const initial_index = self.local_index; | 730 | const initial_index = self.local_index; |
| 703 | switch (ty.zigTypeTag()) { | 731 | const valtype = try self.genValtype(ty); |
| 704 | .Struct => { | 732 | try self.locals.append(self.gpa, valtype); |
| 705 | // for each struct field, generate a local | 733 | self.local_index += 1; |
| 706 | const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data; | 734 | return WValue{ .local = initial_index }; |
| 707 | const fields_len = @intCast(u32, struct_data.fields.count()); | | |
| 708 | try self.locals.ensureUnusedCapacity(self.gpa, fields_len); | | |
| 709 | for (struct_data.fields.values()) |*value| { | | |
| 710 | const val_type = try self.genValtype(value.ty); | | |
| 711 | self.locals.appendAssumeCapacity(val_type); | | |
| 712 | self.local_index += 1; | | |
| 713 | } | | |
| 714 | return WValue{ .multi_value = .{ | | |
| 715 | .index = initial_index, | | |
| 716 | .count = fields_len, | | |
| 717 | } }; | | |
| 718 | }, | | |
| 719 | .ErrorUnion => { | | |
| 720 | const payload_type = ty.errorUnionPayload(); | | |
| 721 | const val_type = try self.genValtype(payload_type); | | |
| 722 | | | |
| 723 | // we emit the error value as the first local, and the payload as the following. | | |
| 724 | // The first local is also used to find the index of the error and payload. | | |
| 725 | // | | |
| 726 | // TODO: Add support where the payload is a type that contains multiple locals such as a struct. | | |
| 727 | try self.locals.ensureUnusedCapacity(self.gpa, 2); | | |
| 728 | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32 | | |
| 729 | self.locals.appendAssumeCapacity(val_type); | | |
| 730 | self.local_index += 2; | | |
| 731 | | | |
| 732 | return WValue{ .multi_value = .{ | | |
| 733 | .index = initial_index, | | |
| 734 | .count = 2, | | |
| 735 | } }; | | |
| 736 | }, | | |
| 737 | .Optional => { | | |
| 738 | var opt_buf: Type.Payload.ElemType = undefined; | | |
| 739 | const child_type = ty.optionalChild(&opt_buf); | | |
| 740 | if (ty.isPtrLikeOptional()) { | | |
| 741 | return self.fail("TODO: wasm optional pointer", .{}); | | |
| 742 | } | | |
| 743 | | | |
| 744 | try self.locals.ensureUnusedCapacity(self.gpa, 2); | | |
| 745 | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // optional 'tag' for null-checking is always i32 | | |
| 746 | self.locals.appendAssumeCapacity(try self.genValtype(child_type)); | | |
| 747 | self.local_index += 2; | | |
| 748 | | | |
| 749 | return WValue{ .multi_value = .{ | | |
| 750 | .index = initial_index, | | |
| 751 | .count = 2, | | |
| 752 | } }; | | |
| 753 | }, | | |
| 754 | else => { | | |
| 755 | const valtype = try self.genValtype(ty); | | |
| 756 | try self.locals.append(self.gpa, valtype); | | |
| 757 | self.local_index += 1; | | |
| 758 | return WValue{ .local = initial_index }; | | |
| 759 | }, | | |
| 760 | } | | |
| 761 | } | 735 | } |
| 762 | | 736 | |
| 763 | fn genFunctype(self: *Self) InnerError!void { | 737 | fn genFunctype(self: *Self) InnerError!void { |
| ... | @@ -786,17 +760,8 @@ fn genFunctype(self: *Self) InnerError!void { | ... | @@ -786,17 +760,8 @@ fn genFunctype(self: *Self) InnerError!void { |
| 786 | .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)), | 760 | .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)), |
| 787 | .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}), | 761 | .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}), |
| 788 | .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}), | 762 | .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}), |
| 789 | .ErrorUnion => { | | |
| 790 | const val_type = try self.genValtype(return_type.errorUnionPayload()); | | |
| 791 | | | |
| 792 | // write down the amount of return values | | |
| 793 | try leb.writeULEB128(writer, @as(u32, 2)); | | |
| 794 | try writer.writeByte(wasm.valtype(.i32)); // error code is always an i32 integer. | | |
| 795 | try writer.writeByte(val_type); | | |
| 796 | }, | | |
| 797 | else => { | 763 | else => { |
| 798 | try leb.writeULEB128(writer, @as(u32, 1)); | 764 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 799 | // Can we maybe get the source index of the return type? | | |
| 800 | const val_type = try self.genValtype(return_type); | 765 | const val_type = try self.genValtype(return_type); |
| 801 | try writer.writeByte(val_type); | 766 | try writer.writeByte(val_type); |
| 802 | }, | 767 | }, |
| ... | @@ -807,6 +772,12 @@ pub fn genFunc(self: *Self) InnerError!Result { | ... | @@ -807,6 +772,12 @@ pub fn genFunc(self: *Self) InnerError!Result { |
| 807 | try self.genFunctype(); | 772 | try self.genFunctype(); |
| 808 | // TODO: check for and handle death of instructions | 773 | // TODO: check for and handle death of instructions |
| 809 | | 774 | |
| | 775 | var cc_result = try self.resolveCallingConventionValues(self.decl.ty); |
| | 776 | defer cc_result.deinit(self.gpa); |
| | 777 | |
| | 778 | self.args = cc_result.args; |
| | 779 | self.return_value = cc_result.return_value; |
| | 780 | |
| 810 | // Generate MIR for function body | 781 | // Generate MIR for function body |
| 811 | try self.genBody(self.air.getMainBody()); | 782 | try self.genBody(self.air.getMainBody()); |
| 812 | // End of function body | 783 | // End of function body |
| ... | @@ -853,7 +824,7 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -853,7 +824,7 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 853 | if (ty.sentinel()) |sentinel| { | 824 | if (ty.sentinel()) |sentinel| { |
| 854 | try self.code.appendSlice(payload.data); | 825 | try self.code.appendSlice(payload.data); |
| 855 | | 826 | |
| 856 | switch (try self.gen(ty.elemType(), sentinel)) { | 827 | switch (try self.gen(ty.childType(), sentinel)) { |
| 857 | .appended => return Result.appended, | 828 | .appended => return Result.appended, |
| 858 | .externally_managed => |data| { | 829 | .externally_managed => |data| { |
| 859 | try self.code.appendSlice(data); | 830 | try self.code.appendSlice(data); |
| ... | @@ -887,6 +858,110 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -887,6 +858,110 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 887 | } | 858 | } |
| 888 | } | 859 | } |
| 889 | | 860 | |
| | 861 | const CallWValues = struct { |
| | 862 | args: []WValue, |
| | 863 | return_value: WValue, |
| | 864 | |
| | 865 | fn deinit(self: *CallWValues, gpa: *Allocator) void { |
| | 866 | gpa.free(self.args); |
| | 867 | self.* = undefined; |
| | 868 | } |
| | 869 | }; |
| | 870 | |
| | 871 | fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValues { |
| | 872 | const cc = fn_ty.fnCallingConvention(); |
| | 873 | const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); |
| | 874 | defer self.gpa.free(param_types); |
| | 875 | fn_ty.fnParamTypes(param_types); |
| | 876 | var result: CallWValues = .{ |
| | 877 | .args = try self.gpa.alloc(WValue, param_types.len), |
| | 878 | .return_value = .none, |
| | 879 | }; |
| | 880 | errdefer self.gpa.free(result.args); |
| | 881 | switch (cc) { |
| | 882 | .Naked => return result, |
| | 883 | .Unspecified, .C => { |
| | 884 | for (param_types) |ty, ty_index| { |
| | 885 | if (!ty.hasCodeGenBits()) { |
| | 886 | result.args[ty_index] = .{ .none = {} }; |
| | 887 | continue; |
| | 888 | } |
| | 889 | |
| | 890 | result.args[ty_index] = .{ .local = self.local_index }; |
| | 891 | self.local_index += 1; |
| | 892 | } |
| | 893 | |
| | 894 | const ret_ty = fn_ty.fnReturnType(); |
| | 895 | switch (ret_ty.zigTypeTag()) { |
| | 896 | .ErrorUnion, .Optional => result.return_value = try self.allocLocal(Type.initTag(.i32)), |
| | 897 | .Int, .Float, .Bool, .Void, .NoReturn => {}, |
| | 898 | else => return self.fail("TODO: Implement function return type {}", .{ret_ty}), |
| | 899 | } |
| | 900 | |
| | 901 | // Check if we store the result as a pointer to the stack rather than |
| | 902 | // by value |
| | 903 | if (result.return_value != .none) { |
| | 904 | if (self.initial_stack_value == .none) try self.initializeStack(); |
| | 905 | const offset = std.math.cast(u32, ret_ty.abiSize(self.target)) catch { |
| | 906 | return self.fail("Return type '{}' too big for stack frame", .{ret_ty}); |
| | 907 | }; |
| | 908 | |
| | 909 | try self.moveStack(offset, result.return_value.local); |
| | 910 | } |
| | 911 | }, |
| | 912 | else => return self.fail("TODO implement function parameters for cc '{}' on wasm", .{cc}), |
| | 913 | } |
| | 914 | return result; |
| | 915 | } |
| | 916 | |
| | 917 | /// Retrieves the stack pointer's value from the global variable and stores |
| | 918 | /// it in a local |
| | 919 | /// Asserts `initial_stack_value` is `.none` |
| | 920 | fn initializeStack(self: *Self) !void { |
| | 921 | assert(self.initial_stack_value == .none); |
| | 922 | // reserve space for immediate value |
| | 923 | // get stack pointer global |
| | 924 | // TODO: For now, we hardcode the stack pointer to index '0', |
| | 925 | // once the linker is further implemented, we can replace this by inserting |
| | 926 | // a relocation and have the linker resolve the correct index to the stack pointer global. |
| | 927 | // NOTE: relocations of the type GLOBAL_INDEX_LEB are 5-bytes big |
| | 928 | try self.addLabel(.global_get, 0); |
| | 929 | |
| | 930 | // Reserve a local to store the current stack pointer |
| | 931 | // We can later use this local to set the stack pointer back to the value |
| | 932 | // we have stored here. |
| | 933 | self.initial_stack_value = try self.allocLocal(Type.initTag(.i32)); |
| | 934 | |
| | 935 | // save the value to the local |
| | 936 | try self.addLabel(.local_set, self.initial_stack_value.local); |
| | 937 | } |
| | 938 | |
| | 939 | /// Reads the stack pointer from `Context.initial_stack_value` and writes it |
| | 940 | /// to the global stack pointer variable |
| | 941 | fn restoreStackPointer(self: *Self) !void { |
| | 942 | // only restore the pointer if it was initialized |
| | 943 | if (self.initial_stack_value == .none) return; |
| | 944 | // Get the original stack pointer's value |
| | 945 | try self.emitWValue(self.initial_stack_value); |
| | 946 | |
| | 947 | // save its value in the global stack pointer |
| | 948 | try self.addLabel(.global_set, 0); |
| | 949 | } |
| | 950 | |
| | 951 | /// Moves the stack pointer by given `offset` |
| | 952 | /// It does this by retrieving the stack pointer, subtracting `offset` and storing |
| | 953 | /// the result back into the stack pointer. |
| | 954 | fn moveStack(self: *Self, offset: u32, local: u32) !void { |
| | 955 | if (offset == 0) return; |
| | 956 | // TODO: Rather than hardcode the stack pointer to position 0, |
| | 957 | // have the linker resolve its relocation |
| | 958 | try self.addLabel(.global_get, 0); |
| | 959 | try self.addImm32(@bitCast(i32, offset)); |
| | 960 | try self.addTag(.i32_sub); |
| | 961 | try self.addLabel(.local_tee, local); |
| | 962 | try self.addLabel(.global_set, 0); |
| | 963 | } |
| | 964 | |
| 890 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | 965 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 891 | const air_tags = self.air.instructions.items(.tag); | 966 | const air_tags = self.air.instructions.items(.tag); |
| 892 | return switch (air_tags[inst]) { | 967 | return switch (air_tags[inst]) { |
| ... | @@ -965,7 +1040,15 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -965,7 +1040,15 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 965 | fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1040 | fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 966 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1041 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 967 | const operand = self.resolveInst(un_op); | 1042 | const operand = self.resolveInst(un_op); |
| 968 | try self.emitWValue(operand); | 1043 | // result must be stored in the stack and we return a pointer |
| | 1044 | // to the stack instead |
| | 1045 | if (self.return_value != .none) { |
| | 1046 | try self.store(self.return_value, operand, self.decl.ty.fnReturnType(), 0); |
| | 1047 | try self.emitWValue(self.return_value); |
| | 1048 | } else { |
| | 1049 | try self.emitWValue(operand); |
| | 1050 | } |
| | 1051 | try self.restoreStackPointer(); |
| 969 | try self.addTag(.@"return"); | 1052 | try self.addTag(.@"return"); |
| 970 | return .none; | 1053 | return .none; |
| 971 | } | 1054 | } |
| ... | @@ -993,12 +1076,33 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -993,12 +1076,33 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 993 | | 1076 | |
| 994 | try self.addLabel(.call, target.link.wasm.symbol_index); | 1077 | try self.addLabel(.call, target.link.wasm.symbol_index); |
| 995 | | 1078 | |
| 996 | return .none; | 1079 | const ret_ty = target.ty.fnReturnType(); |
| | 1080 | switch (ret_ty.zigTypeTag()) { |
| | 1081 | .Void, .NoReturn => return WValue.none, |
| | 1082 | else => { |
| | 1083 | const result_local = try self.allocLocal(ret_ty); |
| | 1084 | try self.addLabel(.local_set, result_local.local); |
| | 1085 | return result_local; |
| | 1086 | }, |
| | 1087 | } |
| 997 | } | 1088 | } |
| 998 | | 1089 | |
| 999 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1090 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1000 | const elem_type = self.air.typeOfIndex(inst).elemType(); | 1091 | const child_type = self.air.typeOfIndex(inst).childType(); |
| 1001 | return self.allocLocal(elem_type); | 1092 | |
| | 1093 | // Initialize the stack |
| | 1094 | if (self.initial_stack_value == .none) { |
| | 1095 | try self.initializeStack(); |
| | 1096 | } |
| | 1097 | |
| | 1098 | const abi_size = child_type.abiSize(self.target); |
| | 1099 | if (abi_size == 0) return WValue{ .none = {} }; |
| | 1100 | |
| | 1101 | // local, containing the offset to the stack position |
| | 1102 | const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32 |
| | 1103 | try self.moveStack(@intCast(u32, abi_size), local.local); |
| | 1104 | |
| | 1105 | return local; |
| 1002 | } | 1106 | } |
| 1003 | | 1107 | |
| 1004 | fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1108 | fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1006,56 +1110,149 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1006,56 +1110,149 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1006 | | 1110 | |
| 1007 | const lhs = self.resolveInst(bin_op.lhs); | 1111 | const lhs = self.resolveInst(bin_op.lhs); |
| 1008 | const rhs = self.resolveInst(bin_op.rhs); | 1112 | const rhs = self.resolveInst(bin_op.rhs); |
| | 1113 | const ty = self.air.typeOf(bin_op.lhs).childType(); |
| 1009 | | 1114 | |
| 1010 | switch (lhs) { | 1115 | const offset: u32 = switch (lhs) { |
| 1011 | .multi_value => |multi_value| switch (rhs) { | 1116 | .local_with_offset => |with_off| with_off.offset, |
| 1012 | // When assigning a value to a multi_value such as a struct, | 1117 | else => 0, |
| 1013 | // we simply assign the local_index to the rhs one. | 1118 | }; |
| 1014 | // This allows us to update struct fields without having to individually | 1119 | |
| 1015 | // set each local as each field's index will be calculated off the struct's base index | 1120 | try self.store(lhs, rhs, ty, offset); |
| 1016 | .multi_value => self.values.put(self.gpa, Air.refToIndex(bin_op.lhs).?, rhs) catch unreachable, // Instruction does not dominate all uses! | 1121 | return .none; |
| 1017 | .constant, .none => { | 1122 | } |
| 1018 | // emit all values onto the stack if constant | 1123 | |
| 1019 | try self.emitWValue(rhs); | 1124 | fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { |
| 1020 | | 1125 | switch (ty.zigTypeTag()) { |
| 1021 | // for each local, pop the stack value into the local | 1126 | .ErrorUnion, .Optional => { |
| 1022 | // As the last element is on top of the stack, we must populate the locals | 1127 | var buf: Type.Payload.ElemType = undefined; |
| 1023 | // in reverse. | 1128 | const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf); |
| 1024 | var i: u32 = multi_value.count; | 1129 | const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8); |
| 1025 | while (i > 0) : (i -= 1) { | 1130 | const payload_offset = if (ty.zigTypeTag() == .ErrorUnion) |
| 1026 | try self.addLabel(.local_set, multi_value.index + i - 1); | 1131 | @intCast(u32, tag_ty.abiSize(self.target)) |
| 1027 | } | 1132 | else |
| 1028 | }, | 1133 | @intCast(u32, ty.abiSize(self.target) - payload_ty.abiSize(self.target)); |
| 1029 | .local => { | 1134 | |
| 1030 | // This can occur when we wrap a single value into a multi-value, | 1135 | switch (rhs) { |
| 1031 | // such as wrapping a non-optional value into an optional. | 1136 | .constant => { |
| 1032 | // This means we must zero the null-tag, and set the payload. | 1137 | // constant will contain both tag and payload, |
| 1033 | assert(multi_value.count == 2); | 1138 | // so save those in 2 temporary locals before storing them |
| 1034 | // set payload | 1139 | // in memory |
| 1035 | try self.emitWValue(rhs); | 1140 | try self.emitWValue(rhs); |
| 1036 | try self.addLabel(.local_set, multi_value.index + 1); | 1141 | const tag_local = try self.allocLocal(tag_ty); |
| 1037 | }, | 1142 | const payload_local = try self.allocLocal(payload_ty); |
| 1038 | else => unreachable, | 1143 | |
| | 1144 | try self.addLabel(.local_set, payload_local.local); |
| | 1145 | try self.addLabel(.local_set, tag_local.local); |
| | 1146 | |
| | 1147 | try self.store(lhs, tag_local, tag_ty, 0); |
| | 1148 | return try self.store(lhs, payload_local, payload_ty, payload_offset); |
| | 1149 | }, |
| | 1150 | .local => { |
| | 1151 | // Load values from `rhs` stack position and store in `lhs` instead |
| | 1152 | const tag_local = try self.load(rhs, tag_ty, 0); |
| | 1153 | const payload_local = try self.load(rhs, payload_ty, payload_offset); |
| | 1154 | |
| | 1155 | try self.store(lhs, tag_local, tag_ty, 0); |
| | 1156 | return try self.store(lhs, payload_local, payload_ty, payload_offset); |
| | 1157 | }, |
| | 1158 | .local_with_offset => |with_offset| { |
| | 1159 | const tag_local = try self.allocLocal(tag_ty); |
| | 1160 | try self.addImm32(0); |
| | 1161 | try self.store(lhs, tag_local, tag_ty, 0); |
| | 1162 | |
| | 1163 | return try self.store( |
| | 1164 | lhs, |
| | 1165 | .{ .local = with_offset.local }, |
| | 1166 | payload_ty, |
| | 1167 | with_offset.offset, |
| | 1168 | ); |
| | 1169 | }, |
| | 1170 | else => unreachable, |
| | 1171 | } |
| 1039 | }, | 1172 | }, |
| 1040 | .local => |local| { | 1173 | .Struct => { |
| 1041 | try self.emitWValue(rhs); | 1174 | // we are copying a struct with its fields. |
| 1042 | try self.addLabel(.local_set, local); | 1175 | // Replace this with a wasm memcpy instruction once we support that feature. |
| | 1176 | const fields_len = ty.structFieldCount(); |
| | 1177 | var index: usize = 0; |
| | 1178 | while (index < fields_len) : (index += 1) { |
| | 1179 | const field_ty = ty.structFieldType(index); |
| | 1180 | if (!field_ty.hasCodeGenBits()) continue; |
| | 1181 | const field_offset = std.math.cast(u32, ty.structFieldOffset(index, self.target)) catch { |
| | 1182 | return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); |
| | 1183 | }; |
| | 1184 | const field_local = try self.load(rhs, field_ty, field_offset); |
| | 1185 | try self.store(lhs, field_local, field_ty, field_offset); |
| | 1186 | } |
| | 1187 | return; |
| 1043 | }, | 1188 | }, |
| 1044 | else => unreachable, | 1189 | else => {}, |
| 1045 | } | 1190 | } |
| 1046 | return .none; | 1191 | try self.emitWValue(lhs); |
| | 1192 | try self.emitWValue(rhs); |
| | 1193 | const valtype = try self.typeToValtype(ty); |
| | 1194 | const opcode = buildOpcode(.{ |
| | 1195 | .valtype1 = valtype, |
| | 1196 | .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size |
| | 1197 | .op = .store, |
| | 1198 | }); |
| | 1199 | |
| | 1200 | // store rhs value at stack pointer's location in memory |
| | 1201 | const mem_arg_index = try self.addExtra(Mir.MemArg{ |
| | 1202 | .offset = offset, |
| | 1203 | .alignment = ty.abiAlignment(self.target), |
| | 1204 | }); |
| | 1205 | try self.addInst(.{ |
| | 1206 | .tag = Mir.Inst.Tag.fromOpcode(opcode), |
| | 1207 | .data = .{ .payload = mem_arg_index }, |
| | 1208 | }); |
| 1047 | } | 1209 | } |
| 1048 | | 1210 | |
| 1049 | fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1211 | fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1050 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1212 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1051 | return self.resolveInst(ty_op.operand); | 1213 | const operand = self.resolveInst(ty_op.operand); |
| | 1214 | const ty = self.air.getRefType(ty_op.ty); |
| | 1215 | |
| | 1216 | return switch (ty.zigTypeTag()) { |
| | 1217 | .Struct, .ErrorUnion, .Optional => operand, // pass as pointer |
| | 1218 | else => switch (operand) { |
| | 1219 | .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset), |
| | 1220 | else => try self.load(operand, ty, 0), |
| | 1221 | }, |
| | 1222 | }; |
| | 1223 | } |
| | 1224 | |
| | 1225 | fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| | 1226 | // load local's value from memory by its stack position |
| | 1227 | try self.emitWValue(operand); |
| | 1228 | // Build the opcode with the right bitsize |
| | 1229 | const signedness: std.builtin.Signedness = if (ty.isUnsignedInt()) .unsigned else .signed; |
| | 1230 | const opcode = buildOpcode(.{ |
| | 1231 | .valtype1 = try self.typeToValtype(ty), |
| | 1232 | .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size |
| | 1233 | .op = .load, |
| | 1234 | .signedness = signedness, |
| | 1235 | }); |
| | 1236 | |
| | 1237 | const mem_arg_index = try self.addExtra(Mir.MemArg{ |
| | 1238 | .offset = offset, |
| | 1239 | .alignment = ty.abiAlignment(self.target), |
| | 1240 | }); |
| | 1241 | try self.addInst(.{ |
| | 1242 | .tag = Mir.Inst.Tag.fromOpcode(opcode), |
| | 1243 | .data = .{ .payload = mem_arg_index }, |
| | 1244 | }); |
| | 1245 | |
| | 1246 | // store the result in a local |
| | 1247 | const result = try self.allocLocal(ty); |
| | 1248 | try self.addLabel(.local_set, result.local); |
| | 1249 | return result; |
| 1052 | } | 1250 | } |
| 1053 | | 1251 | |
| 1054 | fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1252 | fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1055 | _ = inst; | 1253 | _ = inst; |
| 1056 | // arguments share the index with locals | 1254 | defer self.arg_index += 1; |
| 1057 | defer self.local_index += 1; | 1255 | return self.args[self.arg_index]; |
| 1058 | return WValue{ .local = self.local_index }; | | |
| 1059 | } | 1256 | } |
| 1060 | | 1257 | |
| 1061 | fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | 1258 | fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| ... | @@ -1063,14 +1260,6 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | ... | @@ -1063,14 +1260,6 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1063 | const lhs = self.resolveInst(bin_op.lhs); | 1260 | const lhs = self.resolveInst(bin_op.lhs); |
| 1064 | const rhs = self.resolveInst(bin_op.rhs); | 1261 | const rhs = self.resolveInst(bin_op.rhs); |
| 1065 | | 1262 | |
| 1066 | // it's possible for both lhs and/or rhs to return an offset as well, | | |
| 1067 | // in which case we return the first offset occurrence we find. | | |
| 1068 | const offset = blk: { | | |
| 1069 | if (lhs == .mir_offset) break :blk lhs.mir_offset; | | |
| 1070 | if (rhs == .mir_offset) break :blk rhs.mir_offset; | | |
| 1071 | break :blk self.mir_instructions.len; | | |
| 1072 | }; | | |
| 1073 | | | |
| 1074 | try self.emitWValue(lhs); | 1263 | try self.emitWValue(lhs); |
| 1075 | try self.emitWValue(rhs); | 1264 | try self.emitWValue(rhs); |
| 1076 | | 1265 | |
| ... | @@ -1081,7 +1270,11 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | ... | @@ -1081,7 +1270,11 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1081 | .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned, | 1270 | .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned, |
| 1082 | }); | 1271 | }); |
| 1083 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 1272 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1084 | return WValue{ .mir_offset = offset }; | 1273 | |
| | 1274 | // save the result in a temporary |
| | 1275 | const bin_local = try self.allocLocal(bin_ty); |
| | 1276 | try self.addLabel(.local_set, bin_local.local); |
| | 1277 | return bin_local; |
| 1085 | } | 1278 | } |
| 1086 | | 1279 | |
| 1087 | fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | 1280 | fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| ... | @@ -1089,14 +1282,6 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | ... | @@ -1089,14 +1282,6 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1089 | const lhs = self.resolveInst(bin_op.lhs); | 1282 | const lhs = self.resolveInst(bin_op.lhs); |
| 1090 | const rhs = self.resolveInst(bin_op.rhs); | 1283 | const rhs = self.resolveInst(bin_op.rhs); |
| 1091 | | 1284 | |
| 1092 | // it's possible for both lhs and/or rhs to return an offset as well, | | |
| 1093 | // in which case we return the first offset occurrence we find. | | |
| 1094 | const offset = blk: { | | |
| 1095 | if (lhs == .mir_offset) break :blk lhs.mir_offset; | | |
| 1096 | if (rhs == .mir_offset) break :blk rhs.mir_offset; | | |
| 1097 | break :blk self.mir_instructions.len; | | |
| 1098 | }; | | |
| 1099 | | | |
| 1100 | try self.emitWValue(lhs); | 1285 | try self.emitWValue(lhs); |
| 1101 | try self.emitWValue(rhs); | 1286 | try self.emitWValue(rhs); |
| 1102 | | 1287 | |
| ... | @@ -1135,7 +1320,10 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | ... | @@ -1135,7 +1320,10 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1135 | return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{}); | 1320 | return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{}); |
| 1136 | } | 1321 | } |
| 1137 | | 1322 | |
| 1138 | return WValue{ .mir_offset = offset }; | 1323 | // save the result in a temporary |
| | 1324 | const bin_local = try self.allocLocal(bin_ty); |
| | 1325 | try self.addLabel(.local_set, bin_local.local); |
| | 1326 | return bin_local; |
| 1139 | } | 1327 | } |
| 1140 | | 1328 | |
| 1141 | fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | 1329 | fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| ... | @@ -1176,7 +1364,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1176,7 +1364,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1176 | | 1364 | |
| 1177 | // memory instruction followed by their memarg immediate | 1365 | // memory instruction followed by their memarg immediate |
| 1178 | // memarg ::== x:u32, y:u32 => {align x, offset y} | 1366 | // memarg ::== x:u32, y:u32 => {align x, offset y} |
| 1179 | const extra_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 }); | 1367 | const extra_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 4 }); |
| 1180 | try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } }); | 1368 | try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } }); |
| 1181 | } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()}); | 1369 | } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()}); |
| 1182 | }, | 1370 | }, |
| ... | @@ -1289,21 +1477,29 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1289,21 +1477,29 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1289 | const extra = self.air.extraData(Air.Block, ty_pl.payload); | 1477 | const extra = self.air.extraData(Air.Block, ty_pl.payload); |
| 1290 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; | 1478 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| 1291 | | 1479 | |
| 1292 | try self.startBlock(.block, block_ty, null); | 1480 | // if block_ty is non-empty, we create a register to store the temporary value |
| | 1481 | const block_result: WValue = if (block_ty != wasm.block_empty) |
| | 1482 | try self.allocLocal(self.air.getRefType(ty_pl.ty)) |
| | 1483 | else |
| | 1484 | WValue.none; |
| | 1485 | |
| | 1486 | try self.startBlock(.block, wasm.block_empty); |
| 1293 | // Here we set the current block idx, so breaks know the depth to jump | 1487 | // Here we set the current block idx, so breaks know the depth to jump |
| 1294 | // to when breaking out. | 1488 | // to when breaking out. |
| 1295 | try self.blocks.putNoClobber(self.gpa, inst, self.block_depth); | 1489 | try self.blocks.putNoClobber(self.gpa, inst, .{ |
| | 1490 | .label = self.block_depth, |
| | 1491 | .value = block_result, |
| | 1492 | }); |
| 1296 | try self.genBody(body); | 1493 | try self.genBody(body); |
| 1297 | try self.endBlock(); | 1494 | try self.endBlock(); |
| 1298 | | 1495 | |
| 1299 | return .none; | 1496 | return block_result; |
| 1300 | } | 1497 | } |
| 1301 | | 1498 | |
| 1302 | /// appends a new wasm block to the code section and increases the `block_depth` by 1 | 1499 | /// appends a new wasm block to the code section and increases the `block_depth` by 1 |
| 1303 | fn startBlock(self: *Self, block_tag: wasm.Opcode, valtype: u8, with_offset: ?usize) !void { | 1500 | fn startBlock(self: *Self, block_tag: wasm.Opcode, valtype: u8) !void { |
| 1304 | self.block_depth += 1; | 1501 | self.block_depth += 1; |
| 1305 | const offset = with_offset orelse self.mir_instructions.len; | 1502 | try self.addInst(.{ |
| 1306 | try self.addInstAt(offset, .{ | | |
| 1307 | .tag = Mir.Inst.Tag.fromOpcode(block_tag), | 1503 | .tag = Mir.Inst.Tag.fromOpcode(block_tag), |
| 1308 | .data = .{ .block_type = valtype }, | 1504 | .data = .{ .block_type = valtype }, |
| 1309 | }); | 1505 | }); |
| ... | @@ -1322,7 +1518,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1322,7 +1518,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1322 | | 1518 | |
| 1323 | // result type of loop is always 'noreturn', meaning we can always | 1519 | // result type of loop is always 'noreturn', meaning we can always |
| 1324 | // emit the wasm type 'block_empty'. | 1520 | // emit the wasm type 'block_empty'. |
| 1325 | try self.startBlock(.loop, wasm.block_empty, null); | 1521 | try self.startBlock(.loop, wasm.block_empty); |
| 1326 | try self.genBody(body); | 1522 | try self.genBody(body); |
| 1327 | | 1523 | |
| 1328 | // breaking to the index of a loop block will continue the loop instead | 1524 | // breaking to the index of a loop block will continue the loop instead |
| ... | @@ -1340,19 +1536,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1340,19 +1536,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1340 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | 1536 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; |
| 1341 | // TODO: Handle death instructions for then and else body | 1537 | // TODO: Handle death instructions for then and else body |
| 1342 | | 1538 | |
| 1343 | // insert blocks at the position of `offset` so | | |
| 1344 | // the condition can jump to it | | |
| 1345 | const offset = switch (condition) { | | |
| 1346 | .mir_offset => |offset| offset, | | |
| 1347 | else => blk: { | | |
| 1348 | const offset = self.mir_instructions.len; | | |
| 1349 | try self.emitWValue(condition); | | |
| 1350 | break :blk offset; | | |
| 1351 | }, | | |
| 1352 | }; | | |
| 1353 | | | |
| 1354 | // result type is always noreturn, so use `block_empty` as type. | 1539 | // result type is always noreturn, so use `block_empty` as type. |
| 1355 | try self.startBlock(.block, wasm.block_empty, offset); | 1540 | try self.startBlock(.block, wasm.block_empty); |
| | 1541 | // emit the conditional value |
| | 1542 | try self.emitWValue(condition); |
| 1356 | | 1543 | |
| 1357 | // we inserted the block in front of the condition | 1544 | // we inserted the block in front of the condition |
| 1358 | // so now check if condition matches. If not, break outside this block | 1545 | // so now check if condition matches. If not, break outside this block |
| ... | @@ -1369,10 +1556,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1369,10 +1556,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1369 | } | 1556 | } |
| 1370 | | 1557 | |
| 1371 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue { | 1558 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue { |
| 1372 | // save offset, so potential conditions can insert blocks in front of | | |
| 1373 | // the comparison that we can later jump back to | | |
| 1374 | const offset = self.mir_instructions.len; | | |
| 1375 | | | |
| 1376 | const data: Air.Inst.Data = self.air.instructions.items(.data)[inst]; | 1559 | const data: Air.Inst.Data = self.air.instructions.items(.data)[inst]; |
| 1377 | const lhs = self.resolveInst(data.bin_op.lhs); | 1560 | const lhs = self.resolveInst(data.bin_op.lhs); |
| 1378 | const rhs = self.resolveInst(data.bin_op.rhs); | 1561 | const rhs = self.resolveInst(data.bin_op.rhs); |
| ... | @@ -1401,20 +1584,28 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner | ... | @@ -1401,20 +1584,28 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner |
| 1401 | .signedness = signedness, | 1584 | .signedness = signedness, |
| 1402 | }); | 1585 | }); |
| 1403 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 1586 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1404 | return WValue{ .mir_offset = offset }; | 1587 | |
| | 1588 | const cmp_tmp = try self.allocLocal(lhs_ty); |
| | 1589 | try self.addLabel(.local_set, cmp_tmp.local); |
| | 1590 | return cmp_tmp; |
| 1405 | } | 1591 | } |
| 1406 | | 1592 | |
| 1407 | fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1593 | fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1408 | const br = self.air.instructions.items(.data)[inst].br; | 1594 | const br = self.air.instructions.items(.data)[inst].br; |
| | 1595 | const block = self.blocks.get(br.block_inst).?; |
| 1409 | | 1596 | |
| 1410 | // if operand has codegen bits we should break with a value | 1597 | // if operand has codegen bits we should break with a value |
| 1411 | if (self.air.typeOf(br.operand).hasCodeGenBits()) { | 1598 | if (self.air.typeOf(br.operand).hasCodeGenBits()) { |
| 1412 | try self.emitWValue(self.resolveInst(br.operand)); | 1599 | try self.emitWValue(self.resolveInst(br.operand)); |
| | 1600 | |
| | 1601 | if (block.value != .none) { |
| | 1602 | try self.addLabel(.local_set, block.value.local); |
| | 1603 | } |
| 1413 | } | 1604 | } |
| 1414 | | 1605 | |
| 1415 | // We map every block to its block index. | 1606 | // We map every block to its block index. |
| 1416 | // We then determine how far we have to jump to it by subtracting it from current block depth | 1607 | // We then determine how far we have to jump to it by subtracting it from current block depth |
| 1417 | const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?; | 1608 | const idx: u32 = self.block_depth - block.label; |
| 1418 | try self.addLabel(.br, idx); | 1609 | try self.addLabel(.br, idx); |
| 1419 | | 1610 | |
| 1420 | return .none; | 1611 | return .none; |
| ... | @@ -1422,7 +1613,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1422,7 +1613,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1422 | | 1613 | |
| 1423 | fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1614 | fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1424 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1615 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1425 | const offset = self.mir_instructions.len; | | |
| 1426 | | 1616 | |
| 1427 | const operand = self.resolveInst(ty_op.operand); | 1617 | const operand = self.resolveInst(ty_op.operand); |
| 1428 | try self.emitWValue(operand); | 1618 | try self.emitWValue(operand); |
| ... | @@ -1432,7 +1622,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1432,7 +1622,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1432 | try self.addImm32(0); | 1622 | try self.addImm32(0); |
| 1433 | try self.addTag(.i32_eq); | 1623 | try self.addTag(.i32_eq); |
| 1434 | | 1624 | |
| 1435 | return WValue{ .mir_offset = offset }; | 1625 | // save the result in the local |
| | 1626 | const not_tmp = try self.allocLocal(self.air.getRefType(ty_op.ty)); |
| | 1627 | try self.addLabel(.local_set, not_tmp.local); |
| | 1628 | return not_tmp; |
| 1436 | } | 1629 | } |
| 1437 | | 1630 | |
| 1438 | fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1631 | fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1458,24 +1651,45 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1458,24 +1651,45 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1458 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 1651 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1459 | const extra = self.air.extraData(Air.StructField, ty_pl.payload); | 1652 | const extra = self.air.extraData(Air.StructField, ty_pl.payload); |
| 1460 | const struct_ptr = self.resolveInst(extra.data.struct_operand); | 1653 | const struct_ptr = self.resolveInst(extra.data.struct_operand); |
| 1461 | return structFieldPtr(struct_ptr, extra.data.field_index); | 1654 | const struct_ty = self.air.typeOf(extra.data.struct_operand).childType(); |
| | 1655 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) catch { |
| | 1656 | return self.fail("Field type '{}' too big to fit into stack frame", .{ |
| | 1657 | struct_ty.structFieldType(extra.data.field_index), |
| | 1658 | }); |
| | 1659 | }; |
| | 1660 | return structFieldPtr(struct_ptr, offset); |
| 1462 | } | 1661 | } |
| | 1662 | |
| 1463 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue { | 1663 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue { |
| 1464 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1664 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1465 | const struct_ptr = self.resolveInst(ty_op.operand); | 1665 | const struct_ptr = self.resolveInst(ty_op.operand); |
| 1466 | return structFieldPtr(struct_ptr, index); | 1666 | const struct_ty = self.air.typeOf(ty_op.operand).childType(); |
| | 1667 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch { |
| | 1668 | return self.fail("Field type '{}' too big to fit into stack frame", .{ |
| | 1669 | struct_ty.structFieldType(index), |
| | 1670 | }); |
| | 1671 | }; |
| | 1672 | return structFieldPtr(struct_ptr, offset); |
| 1467 | } | 1673 | } |
| 1468 | fn structFieldPtr(struct_ptr: WValue, index: u32) InnerError!WValue { | 1674 | |
| 1469 | return WValue{ .local = struct_ptr.multi_value.index + index }; | 1675 | fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { |
| | 1676 | return WValue{ .local_with_offset = .{ .local = struct_ptr.local, .offset = offset } }; |
| 1470 | } | 1677 | } |
| 1471 | | 1678 | |
| 1472 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1679 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1473 | if (self.liveness.isUnused(inst)) return WValue.none; | 1680 | if (self.liveness.isUnused(inst)) return WValue.none; |
| 1474 | | 1681 | |
| 1475 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 1682 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1476 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; | 1683 | const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 1477 | const struct_multivalue = self.resolveInst(extra.struct_operand).multi_value; | 1684 | const struct_ty = self.air.typeOf(struct_field.struct_operand); |
| 1478 | return WValue{ .local = struct_multivalue.index + extra.field_index }; | 1685 | const operand = self.resolveInst(struct_field.struct_operand); |
| | 1686 | const field_index = struct_field.field_index; |
| | 1687 | const field_ty = struct_ty.structFieldType(field_index); |
| | 1688 | if (!field_ty.hasCodeGenBits()) return WValue.none; |
| | 1689 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch { |
| | 1690 | return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); |
| | 1691 | }; |
| | 1692 | return try self.load(operand, field_ty, offset); |
| 1479 | } | 1693 | } |
| 1480 | | 1694 | |
| 1481 | fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1695 | fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1521,7 +1735,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1521,7 +1735,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1521 | } | 1735 | } |
| 1522 | | 1736 | |
| 1523 | case_list.appendAssumeCapacity(.{ .values = values, .body = case_body }); | 1737 | case_list.appendAssumeCapacity(.{ .values = values, .body = case_body }); |
| 1524 | try self.startBlock(.block, blocktype, null); | 1738 | try self.startBlock(.block, blocktype); |
| 1525 | } | 1739 | } |
| 1526 | | 1740 | |
| 1527 | // When the highest and lowest values are seperated by '50', | 1741 | // When the highest and lowest values are seperated by '50', |
| ... | @@ -1534,7 +1748,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1534,7 +1748,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1534 | const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; | 1748 | const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; |
| 1535 | const has_else_body = else_body.len != 0; | 1749 | const has_else_body = else_body.len != 0; |
| 1536 | if (has_else_body) { | 1750 | if (has_else_body) { |
| 1537 | try self.startBlock(.block, blocktype, null); | 1751 | try self.startBlock(.block, blocktype); |
| 1538 | } | 1752 | } |
| 1539 | | 1753 | |
| 1540 | if (!is_sparse) { | 1754 | if (!is_sparse) { |
| ... | @@ -1542,7 +1756,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1542,7 +1756,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1542 | // The value 'target' represents the index into the table. | 1756 | // The value 'target' represents the index into the table. |
| 1543 | // Each index in the table represents a label to the branch | 1757 | // Each index in the table represents a label to the branch |
| 1544 | // to jump to. | 1758 | // to jump to. |
| 1545 | try self.startBlock(.block, blocktype, null); | 1759 | try self.startBlock(.block, blocktype); |
| 1546 | try self.emitWValue(target); | 1760 | try self.emitWValue(target); |
| 1547 | if (lowest < 0) { | 1761 | if (lowest < 0) { |
| 1548 | // since br_table works using indexes, starting from '0', we must ensure all values | 1762 | // since br_table works using indexes, starting from '0', we must ensure all values |
| ... | @@ -1598,7 +1812,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1598,7 +1812,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1598 | try self.addLabel(.br_if, 0); | 1812 | try self.addLabel(.br_if, 0); |
| 1599 | } else { | 1813 | } else { |
| 1600 | // in multi-value prongs we must check if any prongs match the target value. | 1814 | // in multi-value prongs we must check if any prongs match the target value. |
| 1601 | try self.startBlock(.block, blocktype, null); | 1815 | try self.startBlock(.block, blocktype); |
| 1602 | for (case.values) |value| { | 1816 | for (case.values) |value| { |
| 1603 | try self.emitWValue(target); | 1817 | try self.emitWValue(target); |
| 1604 | try self.emitConstant(value.value, target_ty); | 1818 | try self.emitConstant(value.value, target_ty); |
| ... | @@ -1629,30 +1843,40 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1629,30 +1843,40 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1629 | fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { | 1843 | fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { |
| 1630 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1844 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1631 | const operand = self.resolveInst(un_op); | 1845 | const operand = self.resolveInst(un_op); |
| 1632 | const offset = self.mir_instructions.len; | 1846 | const err_ty = self.air.typeOf(un_op).errorUnionSet(); |
| | 1847 | |
| | 1848 | // load the error tag value |
| | 1849 | try self.emitWValue(operand); |
| | 1850 | const mem_arg_index = try self.addExtra(Mir.MemArg{ |
| | 1851 | .offset = 0, |
| | 1852 | .alignment = err_ty.abiAlignment(self.target), |
| | 1853 | }); |
| | 1854 | try self.addInst(.{ |
| | 1855 | .tag = .i32_load16_u, |
| | 1856 | .data = .{ .payload = mem_arg_index }, |
| | 1857 | }); |
| 1633 | | 1858 | |
| 1634 | // load the error value which is positioned at multi_value's index | | |
| 1635 | try self.emitWValue(.{ .local = operand.multi_value.index }); | | |
| 1636 | // Compare the error value with '0' | 1859 | // Compare the error value with '0' |
| 1637 | try self.addImm32(0); | 1860 | try self.addImm32(0); |
| 1638 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 1861 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1639 | | 1862 | |
| 1640 | return WValue{ .mir_offset = offset }; | 1863 | const is_err_tmp = try self.allocLocal(err_ty); |
| | 1864 | try self.addLabel(.local_set, is_err_tmp.local); |
| | 1865 | return is_err_tmp; |
| 1641 | } | 1866 | } |
| 1642 | | 1867 | |
| 1643 | fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1868 | fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1644 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1869 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1645 | const operand = self.resolveInst(ty_op.operand); | 1870 | const operand = self.resolveInst(ty_op.operand); |
| 1646 | // The index of multi_value contains the error code. To get the initial index of the payload we get | 1871 | const err_ty = self.air.typeOf(ty_op.operand); |
| 1647 | // the following index. Next, convert it to a `WValue.local` | 1872 | const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target)); |
| 1648 | // | 1873 | return self.load(operand, err_ty.errorUnionPayload(), offset); |
| 1649 | // TODO: Check if payload is a type that requires a multi_value as well and emit that instead. i.e. a struct. | | |
| 1650 | return WValue{ .local = operand.multi_value.index + 1 }; | | |
| 1651 | } | 1874 | } |
| 1652 | | 1875 | |
| 1653 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1876 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1654 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1877 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1655 | return self.resolveInst(ty_op.operand); | 1878 | _ = ty_op; |
| | 1879 | return self.fail("TODO: wasm airWrapErrUnionPayload", .{}); |
| 1656 | } | 1880 | } |
| 1657 | | 1881 | |
| 1658 | fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1882 | fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1682,22 +1906,41 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError! | ... | @@ -1682,22 +1906,41 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError! |
| 1682 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1906 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1683 | const operand = self.resolveInst(un_op); | 1907 | const operand = self.resolveInst(un_op); |
| 1684 | | 1908 | |
| 1685 | // load the null value which is positioned at multi_value's index | 1909 | // load the null tag value |
| 1686 | try self.emitWValue(.{ .local = operand.multi_value.index }); | 1910 | try self.emitWValue(operand); |
| | 1911 | const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 1 }); |
| | 1912 | try self.addInst(.{ |
| | 1913 | .tag = .i32_load8_u, |
| | 1914 | .data = .{ .payload = mem_arg_index }, |
| | 1915 | }); |
| | 1916 | |
| | 1917 | // Compare the error value with '0' |
| 1687 | try self.addImm32(0); | 1918 | try self.addImm32(0); |
| 1688 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 1919 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1689 | | 1920 | |
| 1690 | // we save the result in a new local | 1921 | const is_null_tmp = try self.allocLocal(Type.initTag(.u8)); |
| 1691 | const local = try self.allocLocal(Type.initTag(.i32)); | 1922 | try self.addLabel(.local_set, is_null_tmp.local); |
| 1692 | try self.addLabel(.local_set, local.local); | 1923 | return is_null_tmp; |
| 1693 | | | |
| 1694 | return local; | | |
| 1695 | } | 1924 | } |
| 1696 | | 1925 | |
| 1697 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1926 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1698 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1927 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1699 | const operand = self.resolveInst(ty_op.operand); | 1928 | const operand = self.resolveInst(ty_op.operand); |
| 1700 | return WValue{ .local = operand.multi_value.index + 1 }; | 1929 | const opt_ty = self.air.typeOf(ty_op.operand); |
| | 1930 | |
| | 1931 | // For pointers we simply return its stack address, rather than |
| | 1932 | // loading its value |
| | 1933 | if (opt_ty.zigTypeTag() == .Pointer) { |
| | 1934 | return WValue{ .local_with_offset = .{ .local = operand.local, .offset = 1 } }; |
| | 1935 | } |
| | 1936 | |
| | 1937 | if (opt_ty.isPtrLikeOptional()) return operand; |
| | 1938 | |
| | 1939 | var buf: Type.Payload.ElemType = undefined; |
| | 1940 | const child_ty = opt_ty.optionalChild(&buf); |
| | 1941 | const offset = opt_ty.abiSize(self.target) - child_ty.abiSize(self.target); |
| | 1942 | |
| | 1943 | return self.load(operand, child_ty, @intCast(u32, offset)); |
| 1701 | } | 1944 | } |
| 1702 | | 1945 | |
| 1703 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1946 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1709,5 +1952,14 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue | ... | @@ -1709,5 +1952,14 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue |
| 1709 | | 1952 | |
| 1710 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1953 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1711 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1954 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1712 | return self.resolveInst(ty_op.operand); | 1955 | const operand = self.resolveInst(ty_op.operand); |
| | 1956 | |
| | 1957 | const op_ty = self.air.typeOf(ty_op.operand); |
| | 1958 | const optional_ty = self.air.getRefType(ty_op.ty); |
| | 1959 | const offset = optional_ty.abiSize(self.target) - op_ty.abiSize(self.target); |
| | 1960 | |
| | 1961 | return WValue{ .local_with_offset = .{ |
| | 1962 | .local = operand.local, |
| | 1963 | .offset = @intCast(u32, offset), |
| | 1964 | } }; |
| 1713 | } | 1965 | } |