| ... | ... | @@ -514,6 +514,9 @@ func_type_data: ArrayList(u8), |
| 514 | 514 | /// NOTE: arguments share the index with locals therefore the first variable |
| 515 | 515 | /// will have the index that comes after the last argument's index |
| 516 | 516 | local_index: u32 = 0, |
| 517 | /// The index of the current argument. |
| 518 | /// Used to track which argument is being referenced in `airArg`. |
| 519 | arg_index: u32 = 0, |
| 517 | 520 | /// If codegen fails, an error messages will be allocated and saved in `err_msg` |
| 518 | 521 | err_msg: *Module.ErrorMsg, |
| 519 | 522 | /// Current block depth. Used to calculate the relative difference between a break |
| ... | ... | @@ -537,6 +540,13 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| 537 | 540 | /// When a function is executing, we store the the current stack pointer's value within this local. |
| 538 | 541 | /// This value is then used to restore the stack pointer to the original value at the return of the function. |
| 539 | 542 | initial_stack_value: WValue = .none, |
| 543 | /// Arguments of this function declaration |
| 544 | /// This will be set after `resolveCallingConventionValues` |
| 545 | args: []WValue = undefined, |
| 546 | /// This will only be `.none` if the function returns void, or returns an immediate. |
| 547 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated |
| 548 | /// before this function returns its execution to the caller. |
| 549 | return_value: WValue = .none, |
| 540 | 550 | |
| 541 | 551 | const InnerError = error{ |
| 542 | 552 | OutOfMemory, |
| ... | ... | @@ -736,17 +746,8 @@ fn genFunctype(self: *Self) InnerError!void { |
| 736 | 746 | .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)), |
| 737 | 747 | .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}), |
| 738 | 748 | .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}), |
| 739 | | .ErrorUnion => { |
| 740 | | const val_type = try self.genValtype(return_type.errorUnionPayload()); |
| 741 | | |
| 742 | | // write down the amount of return values |
| 743 | | try leb.writeULEB128(writer, @as(u32, 2)); |
| 744 | | try writer.writeByte(wasm.valtype(.i32)); // error code is always an i32 integer. |
| 745 | | try writer.writeByte(val_type); |
| 746 | | }, |
| 747 | 749 | else => { |
| 748 | 750 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 749 | | // Can we maybe get the source index of the return type? |
| 750 | 751 | const val_type = try self.genValtype(return_type); |
| 751 | 752 | try writer.writeByte(val_type); |
| 752 | 753 | }, |
| ... | ... | @@ -757,6 +758,12 @@ pub fn genFunc(self: *Self) InnerError!Result { |
| 757 | 758 | try self.genFunctype(); |
| 758 | 759 | // TODO: check for and handle death of instructions |
| 759 | 760 | |
| 761 | var cc_result = try self.resolveCallingConventionValues(self.decl.ty); |
| 762 | defer cc_result.deinit(self.gpa); |
| 763 | |
| 764 | self.args = cc_result.args; |
| 765 | self.return_value = cc_result.return_value; |
| 766 | |
| 760 | 767 | // Generate MIR for function body |
| 761 | 768 | try self.genBody(self.air.getMainBody()); |
| 762 | 769 | // End of function body |
| ... | ... | @@ -836,9 +843,67 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 836 | 843 | } |
| 837 | 844 | } |
| 838 | 845 | |
| 846 | const CallWValues = struct { |
| 847 | args: []WValue, |
| 848 | return_value: WValue, |
| 849 | |
| 850 | fn deinit(self: *CallWValues, gpa: *Allocator) void { |
| 851 | gpa.free(self.args); |
| 852 | self.* = undefined; |
| 853 | } |
| 854 | }; |
| 855 | |
| 856 | fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValues { |
| 857 | const cc = fn_ty.fnCallingConvention(); |
| 858 | const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); |
| 859 | defer self.gpa.free(param_types); |
| 860 | fn_ty.fnParamTypes(param_types); |
| 861 | var result: CallWValues = .{ |
| 862 | .args = try self.gpa.alloc(WValue, param_types.len), |
| 863 | .return_value = .none, |
| 864 | }; |
| 865 | errdefer self.gpa.free(result.args); |
| 866 | switch (cc) { |
| 867 | .Naked => return result, |
| 868 | .Unspecified, .C => { |
| 869 | for (param_types) |ty, ty_index| { |
| 870 | if (!ty.hasCodeGenBits()) { |
| 871 | result.args[ty_index] = .{ .none = {} }; |
| 872 | continue; |
| 873 | } |
| 874 | |
| 875 | result.args[ty_index] = .{ .local = self.local_index }; |
| 876 | self.local_index += 1; |
| 877 | } |
| 878 | |
| 879 | const ret_ty = fn_ty.fnReturnType(); |
| 880 | switch (ret_ty.zigTypeTag()) { |
| 881 | .ErrorUnion => result.return_value = try self.allocLocal(Type.initTag(.i32)), |
| 882 | .Int, .Float, .Bool, .Void, .NoReturn => {}, |
| 883 | else => return self.fail("TODO: Implement function return type {}", .{ret_ty}), |
| 884 | } |
| 885 | |
| 886 | // Check if we store the result as a pointer to the stack rather than |
| 887 | // by value |
| 888 | if (result.return_value != .none) { |
| 889 | if (self.initial_stack_value == .none) try self.initializeStack(); |
| 890 | const offset = std.math.cast(u32, ret_ty.abiSize(self.target)) catch { |
| 891 | return self.fail("Return type '{}' too big for stack frame", .{ret_ty}); |
| 892 | }; |
| 893 | |
| 894 | try self.moveStack(offset, result.return_value.local); |
| 895 | } |
| 896 | }, |
| 897 | else => return self.fail("TODO implement function parameters for cc '{}' on wasm", .{cc}), |
| 898 | } |
| 899 | return result; |
| 900 | } |
| 901 | |
| 839 | 902 | /// Retrieves the stack pointer's value from the global variable and stores |
| 840 | 903 | /// it in a local |
| 904 | /// Asserts `initial_stack_value` is `.none` |
| 841 | 905 | fn initializeStack(self: *Self) !void { |
| 906 | assert(self.initial_stack_value == .none); |
| 842 | 907 | // reserve space for immediate value |
| 843 | 908 | // get stack pointer global |
| 844 | 909 | // TODO: For now, we hardcode the stack pointer to index '0', |
| ... | ... | @@ -917,8 +982,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 917 | 982 | .dbg_stmt => WValue.none, |
| 918 | 983 | .intcast => self.airIntcast(inst), |
| 919 | 984 | |
| 920 | | .is_err => self.airIsErr(inst, .i32_ne), |
| 921 | | .is_non_err => self.airIsErr(inst, .i32_eq), |
| 985 | .is_err => self.airIsErr(inst, .i32_eq), |
| 986 | .is_non_err => self.airIsErr(inst, .i32_ne), |
| 922 | 987 | |
| 923 | 988 | .is_null => self.airIsNull(inst, .i32_ne), |
| 924 | 989 | .is_non_null => self.airIsNull(inst, .i32_eq), |
| ... | ... | @@ -960,7 +1025,14 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 960 | 1025 | fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 961 | 1026 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 962 | 1027 | const operand = self.resolveInst(un_op); |
| 963 | | try self.emitWValue(operand); |
| 1028 | // result must be stored in the stack and we return a pointer |
| 1029 | // to the stack instead |
| 1030 | if (self.return_value != .none) { |
| 1031 | try self.store(self.return_value, operand, self.decl.ty.fnReturnType(), 0); |
| 1032 | try self.emitWValue(self.return_value); |
| 1033 | } else { |
| 1034 | try self.emitWValue(operand); |
| 1035 | } |
| 964 | 1036 | try self.restoreStackPointer(); |
| 965 | 1037 | try self.addTag(.@"return"); |
| 966 | 1038 | return .none; |
| ... | ... | @@ -988,7 +1060,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 988 | 1060 | } |
| 989 | 1061 | |
| 990 | 1062 | try self.addLabel(.call, target.link.wasm.symbol_index); |
| 991 | | return .none; |
| 1063 | |
| 1064 | const ret_ty = target.ty.fnReturnType(); |
| 1065 | switch (ret_ty.zigTypeTag()) { |
| 1066 | .ErrorUnion => { |
| 1067 | const result_local = try self.allocLocal(ret_ty); |
| 1068 | try self.addLabel(.local_set, result_local.local); |
| 1069 | return result_local; |
| 1070 | }, |
| 1071 | else => return WValue.none, |
| 1072 | } |
| 992 | 1073 | } |
| 993 | 1074 | |
| 994 | 1075 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -1021,6 +1102,11 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1021 | 1102 | else => 0, |
| 1022 | 1103 | }; |
| 1023 | 1104 | |
| 1105 | try self.store(lhs, rhs, ty, offset); |
| 1106 | return .none; |
| 1107 | } |
| 1108 | |
| 1109 | fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { |
| 1024 | 1110 | switch (ty.zigTypeTag()) { |
| 1025 | 1111 | .ErrorUnion, .Optional => { |
| 1026 | 1112 | var buf: Type.Payload.ElemType = undefined; |
| ... | ... | @@ -1039,22 +1125,18 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1039 | 1125 | try self.addLabel(.local_set, tag_local.local); |
| 1040 | 1126 | |
| 1041 | 1127 | try self.store(lhs, tag_local, tag_ty, 0); |
| 1042 | | try self.store(lhs, payload_local, payload_ty, payload_offset); |
| 1043 | | } else if (offset == 0) { |
| 1044 | | // tag is being set |
| 1045 | | try self.store(lhs, rhs, tag_ty, 0); |
| 1128 | return try self.store(lhs, payload_local, payload_ty, payload_offset); |
| 1046 | 1129 | } else { |
| 1047 | | // payload is being set |
| 1048 | | try self.store(lhs, rhs, payload_ty, payload_offset); |
| 1130 | // Load values from `rhs` stack position and store in `lhs` instead |
| 1131 | const tag_local = try self.load(rhs, tag_ty, 0); |
| 1132 | const payload_local = try self.load(rhs, payload_ty, payload_offset); |
| 1133 | |
| 1134 | try self.store(lhs, tag_local, tag_ty, 0); |
| 1135 | return try self.store(lhs, payload_local, payload_ty, payload_offset); |
| 1049 | 1136 | } |
| 1050 | 1137 | }, |
| 1051 | | else => try self.store(lhs, rhs, ty, offset), |
| 1138 | else => {}, |
| 1052 | 1139 | } |
| 1053 | | |
| 1054 | | return .none; |
| 1055 | | } |
| 1056 | | |
| 1057 | | fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { |
| 1058 | 1140 | try self.emitWValue(lhs); |
| 1059 | 1141 | try self.emitWValue(rhs); |
| 1060 | 1142 | const valtype = try self.typeToValtype(ty); |