| ... | @@ -514,6 +514,9 @@ func_type_data: ArrayList(u8), | ... | @@ -514,6 +514,9 @@ func_type_data: ArrayList(u8), |
| 514 | /// NOTE: arguments share the index with locals therefore the first variable | 514 | /// NOTE: arguments share the index with locals therefore the first variable |
| 515 | /// will have the index that comes after the last argument's index | 515 | /// will have the index that comes after the last argument's index |
| 516 | local_index: u32 = 0, | 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 | /// If codegen fails, an error messages will be allocated and saved in `err_msg` | 520 | /// If codegen fails, an error messages will be allocated and saved in `err_msg` |
| 518 | err_msg: *Module.ErrorMsg, | 521 | err_msg: *Module.ErrorMsg, |
| 519 | /// Current block depth. Used to calculate the relative difference between a break | 522 | /// Current block depth. Used to calculate the relative difference between a break |
| ... | @@ -537,6 +540,13 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{}, | ... | @@ -537,6 +540,13 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| 537 | /// When a function is executing, we store the the current stack pointer's value within this local. | 540 | /// When a function is executing, we store the the current stack pointer's value within this local. |
| 538 | /// This value is then used to restore the stack pointer to the original value at the return of the function. | 541 | /// This value is then used to restore the stack pointer to the original value at the return of the function. |
| 539 | initial_stack_value: WValue = .none, | 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 | const InnerError = error{ | 551 | const InnerError = error{ |
| 542 | OutOfMemory, | 552 | OutOfMemory, |
| ... | @@ -736,17 +746,8 @@ fn genFunctype(self: *Self) InnerError!void { | ... | @@ -736,17 +746,8 @@ fn genFunctype(self: *Self) InnerError!void { |
| 736 | .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)), | 746 | .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)), |
| 737 | .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}), | 747 | .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}), |
| 738 | .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}), | 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 | else => { | 749 | else => { |
| 748 | try leb.writeULEB128(writer, @as(u32, 1)); | 750 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 749 | // Can we maybe get the source index of the return type? | | |
| 750 | const val_type = try self.genValtype(return_type); | 751 | const val_type = try self.genValtype(return_type); |
| 751 | try writer.writeByte(val_type); | 752 | try writer.writeByte(val_type); |
| 752 | }, | 753 | }, |
| ... | @@ -757,6 +758,12 @@ pub fn genFunc(self: *Self) InnerError!Result { | ... | @@ -757,6 +758,12 @@ pub fn genFunc(self: *Self) InnerError!Result { |
| 757 | try self.genFunctype(); | 758 | try self.genFunctype(); |
| 758 | // TODO: check for and handle death of instructions | 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 | // Generate MIR for function body | 767 | // Generate MIR for function body |
| 761 | try self.genBody(self.air.getMainBody()); | 768 | try self.genBody(self.air.getMainBody()); |
| 762 | // End of function body | 769 | // End of function body |
| ... | @@ -836,9 +843,67 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -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 | /// Retrieves the stack pointer's value from the global variable and stores | 902 | /// Retrieves the stack pointer's value from the global variable and stores |
| 840 | /// it in a local | 903 | /// it in a local |
| | 904 | /// Asserts `initial_stack_value` is `.none` |
| 841 | fn initializeStack(self: *Self) !void { | 905 | fn initializeStack(self: *Self) !void { |
| | 906 | assert(self.initial_stack_value == .none); |
| 842 | // reserve space for immediate value | 907 | // reserve space for immediate value |
| 843 | // get stack pointer global | 908 | // get stack pointer global |
| 844 | // TODO: For now, we hardcode the stack pointer to index '0', | 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,8 +982,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 917 | .dbg_stmt => WValue.none, | 982 | .dbg_stmt => WValue.none, |
| 918 | .intcast => self.airIntcast(inst), | 983 | .intcast => self.airIntcast(inst), |
| 919 | | 984 | |
| 920 | .is_err => self.airIsErr(inst, .i32_ne), | 985 | .is_err => self.airIsErr(inst, .i32_eq), |
| 921 | .is_non_err => self.airIsErr(inst, .i32_eq), | 986 | .is_non_err => self.airIsErr(inst, .i32_ne), |
| 922 | | 987 | |
| 923 | .is_null => self.airIsNull(inst, .i32_ne), | 988 | .is_null => self.airIsNull(inst, .i32_ne), |
| 924 | .is_non_null => self.airIsNull(inst, .i32_eq), | 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,7 +1025,14 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 960 | fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1025 | fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 961 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1026 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 962 | const operand = self.resolveInst(un_op); | 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 | try self.restoreStackPointer(); | 1036 | try self.restoreStackPointer(); |
| 965 | try self.addTag(.@"return"); | 1037 | try self.addTag(.@"return"); |
| 966 | return .none; | 1038 | return .none; |
| ... | @@ -988,7 +1060,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -988,7 +1060,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 988 | } | 1060 | } |
| 989 | | 1061 | |
| 990 | try self.addLabel(.call, target.link.wasm.symbol_index); | 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 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 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,6 +1102,11 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1021 | else => 0, | 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 | switch (ty.zigTypeTag()) { | 1110 | switch (ty.zigTypeTag()) { |
| 1025 | .ErrorUnion, .Optional => { | 1111 | .ErrorUnion, .Optional => { |
| 1026 | var buf: Type.Payload.ElemType = undefined; | 1112 | var buf: Type.Payload.ElemType = undefined; |
| ... | @@ -1039,22 +1125,18 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1039,22 +1125,18 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1039 | try self.addLabel(.local_set, tag_local.local); | 1125 | try self.addLabel(.local_set, tag_local.local); |
| 1040 | | 1126 | |
| 1041 | try self.store(lhs, tag_local, tag_ty, 0); | 1127 | try self.store(lhs, tag_local, tag_ty, 0); |
| 1042 | try self.store(lhs, payload_local, payload_ty, payload_offset); | 1128 | return 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); | | |
| 1046 | } else { | 1129 | } else { |
| 1047 | // payload is being set | 1130 | // Load values from `rhs` stack position and store in `lhs` instead |
| 1048 | try self.store(lhs, rhs, payload_ty, payload_offset); | 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 | try self.emitWValue(lhs); | 1140 | try self.emitWValue(lhs); |
| 1059 | try self.emitWValue(rhs); | 1141 | try self.emitWValue(rhs); |
| 1060 | const valtype = try self.typeToValtype(ty); | 1142 | const valtype = try self.typeToValtype(ty); |