| ... | @@ -850,6 +850,122 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -850,6 +850,122 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 850 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 850 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 851 | } | 851 | } |
| 852 | | 852 | |
| | 853 | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| | 854 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| | 855 | const lhs = try self.resolveInst(bin_op.lhs); |
| | 856 | const rhs = try self.resolveInst(bin_op.rhs); |
| | 857 | const lhs_ty = self.typeOf(bin_op.lhs); |
| | 858 | const rhs_ty = self.typeOf(bin_op.rhs); |
| | 859 | |
| | 860 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 861 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| | 862 | } |
| | 863 | |
| | 864 | /// For all your binary operation needs, this function will generate |
| | 865 | /// the corresponding Mir instruction(s). Returns the location of the |
| | 866 | /// result. |
| | 867 | /// |
| | 868 | /// If the binary operation itself happens to be an Air instruction, |
| | 869 | /// pass the corresponding index in the inst parameter. That helps |
| | 870 | /// this function do stuff like reusing operands. |
| | 871 | /// |
| | 872 | /// This function does not do any lowering to Mir itself, but instead |
| | 873 | /// looks at the lhs and rhs and determines which kind of lowering |
| | 874 | /// would be best suitable and then delegates the lowering to other |
| | 875 | /// functions. |
| | 876 | /// |
| | 877 | /// `maybe_inst` **needs** to be a bin_op, make sure of that. |
| | 878 | fn binOp( |
| | 879 | self: *Self, |
| | 880 | tag: Air.Inst.Tag, |
| | 881 | maybe_inst: ?Air.Inst.Index, |
| | 882 | lhs: MCValue, |
| | 883 | rhs: MCValue, |
| | 884 | lhs_ty: Type, |
| | 885 | rhs_ty: Type, |
| | 886 | ) InnerError!MCValue { |
| | 887 | const mod = self.bin_file.comp.module.?; |
| | 888 | switch (tag) { |
| | 889 | // Arithmetic operations on integers and floats |
| | 890 | .add, |
| | 891 | .sub, |
| | 892 | .cmp_eq, |
| | 893 | .cmp_neq, |
| | 894 | .cmp_gt, |
| | 895 | .cmp_gte, |
| | 896 | .cmp_lt, |
| | 897 | .cmp_lte, |
| | 898 | => { |
| | 899 | switch (lhs_ty.zigTypeTag(mod)) { |
| | 900 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| | 901 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| | 902 | .Int => { |
| | 903 | assert(lhs_ty.eql(rhs_ty, mod)); |
| | 904 | const int_info = lhs_ty.intInfo(mod); |
| | 905 | if (int_info.bits <= 64) { |
| | 906 | if (rhs == .immediate) { |
| | 907 | return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 908 | } |
| | 909 | return self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 910 | } else { |
| | 911 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| | 912 | } |
| | 913 | }, |
| | 914 | else => unreachable, |
| | 915 | } |
| | 916 | }, |
| | 917 | .ptr_add, |
| | 918 | .ptr_sub, |
| | 919 | => { |
| | 920 | switch (lhs_ty.zigTypeTag(mod)) { |
| | 921 | .Pointer => { |
| | 922 | const ptr_ty = lhs_ty; |
| | 923 | const elem_ty = switch (ptr_ty.ptrSize(mod)) { |
| | 924 | .One => ptr_ty.childType(mod).childType(mod), // ptr to array, so get array element type |
| | 925 | else => ptr_ty.childType(mod), |
| | 926 | }; |
| | 927 | const elem_size = elem_ty.abiSize(mod); |
| | 928 | |
| | 929 | if (elem_size == 1) { |
| | 930 | const base_tag: Air.Inst.Tag = switch (tag) { |
| | 931 | .ptr_add => .add, |
| | 932 | .ptr_sub => .sub, |
| | 933 | else => unreachable, |
| | 934 | }; |
| | 935 | |
| | 936 | return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 937 | } else { |
| | 938 | return self.fail("TODO ptr_add with elem_size > 1", .{}); |
| | 939 | } |
| | 940 | }, |
| | 941 | else => unreachable, |
| | 942 | } |
| | 943 | }, |
| | 944 | |
| | 945 | // These instructions have unsymteric bit sizes on RHS and LHS. |
| | 946 | .shr, |
| | 947 | .shl, |
| | 948 | => { |
| | 949 | switch (lhs_ty.zigTypeTag(mod)) { |
| | 950 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| | 951 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| | 952 | .Int => { |
| | 953 | const int_info = lhs_ty.intInfo(mod); |
| | 954 | if (int_info.bits <= 64) { |
| | 955 | if (rhs == .immediate) { |
| | 956 | return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 957 | } |
| | 958 | return self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 959 | } else { |
| | 960 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| | 961 | } |
| | 962 | }, |
| | 963 | else => unreachable, |
| | 964 | } |
| | 965 | }, |
| | 966 | else => unreachable, |
| | 967 | } |
| | 968 | } |
| 853 | /// Don't call this function directly. Use binOp instead. | 969 | /// Don't call this function directly. Use binOp instead. |
| 854 | /// | 970 | /// |
| 855 | /// Calling this function signals an intention to generate a Mir | 971 | /// Calling this function signals an intention to generate a Mir |
| ... | @@ -963,7 +1079,6 @@ fn binOpImm( | ... | @@ -963,7 +1079,6 @@ fn binOpImm( |
| 963 | lhs_ty: Type, | 1079 | lhs_ty: Type, |
| 964 | rhs_ty: Type, | 1080 | rhs_ty: Type, |
| 965 | ) !MCValue { | 1081 | ) !MCValue { |
| 966 | _ = rhs_ty; | | |
| 967 | assert(rhs == .immediate); | 1082 | assert(rhs == .immediate); |
| 968 | | 1083 | |
| 969 | const lhs_is_register = lhs == .register; | 1084 | const lhs_is_register = lhs == .register; |
| ... | @@ -1006,142 +1121,44 @@ fn binOpImm( | ... | @@ -1006,142 +1121,44 @@ fn binOpImm( |
| 1006 | const mir_tag: Mir.Inst.Tag = switch (tag) { | 1121 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1007 | .shl => .slli, | 1122 | .shl => .slli, |
| 1008 | .shr => .srli, | 1123 | .shr => .srli, |
| | 1124 | .cmp_gte => .cmp_imm_gte, |
| 1009 | else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}), | 1125 | else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}), |
| 1010 | }; | 1126 | }; |
| 1011 | | 1127 | |
| 1012 | _ = try self.addInst(.{ | 1128 | // apply some special operations needed |
| 1013 | .tag = mir_tag, | 1129 | switch (mir_tag) { |
| 1014 | .data = .{ | 1130 | .slli, |
| 1015 | .i_type = .{ | 1131 | .srli, |
| 1016 | .rd = dest_reg, | | |
| 1017 | .rs1 = lhs_reg, | | |
| 1018 | .imm12 = math.cast(i12, rhs.immediate) orelse { | | |
| 1019 | return self.fail("TODO: binOpImm larger than i12 i_type payload", .{}); | | |
| 1020 | }, | | |
| 1021 | }, | | |
| 1022 | }, | | |
| 1023 | }); | | |
| 1024 | | | |
| 1025 | // generate the struct for OF checks | | |
| 1026 | | | |
| 1027 | return MCValue{ .register = dest_reg }; | | |
| 1028 | } | | |
| 1029 | | | |
| 1030 | /// For all your binary operation needs, this function will generate | | |
| 1031 | /// the corresponding Mir instruction(s). Returns the location of the | | |
| 1032 | /// result. | | |
| 1033 | /// | | |
| 1034 | /// If the binary operation itself happens to be an Air instruction, | | |
| 1035 | /// pass the corresponding index in the inst parameter. That helps | | |
| 1036 | /// this function do stuff like reusing operands. | | |
| 1037 | /// | | |
| 1038 | /// This function does not do any lowering to Mir itself, but instead | | |
| 1039 | /// looks at the lhs and rhs and determines which kind of lowering | | |
| 1040 | /// would be best suitable and then delegates the lowering to other | | |
| 1041 | /// functions. | | |
| 1042 | /// | | |
| 1043 | /// `maybe_inst` **needs** to be a bin_op, make sure of that. | | |
| 1044 | fn binOp( | | |
| 1045 | self: *Self, | | |
| 1046 | tag: Air.Inst.Tag, | | |
| 1047 | maybe_inst: ?Air.Inst.Index, | | |
| 1048 | lhs: MCValue, | | |
| 1049 | rhs: MCValue, | | |
| 1050 | lhs_ty: Type, | | |
| 1051 | rhs_ty: Type, | | |
| 1052 | ) InnerError!MCValue { | | |
| 1053 | const mod = self.bin_file.comp.module.?; | | |
| 1054 | switch (tag) { | | |
| 1055 | // Arithmetic operations on integers and floats | | |
| 1056 | .add, | | |
| 1057 | .sub, | | |
| 1058 | .cmp_eq, | | |
| 1059 | .cmp_neq, | | |
| 1060 | .cmp_gt, | | |
| 1061 | .cmp_gte, | | |
| 1062 | .cmp_lt, | | |
| 1063 | .cmp_lte, | | |
| 1064 | => { | 1132 | => { |
| 1065 | switch (lhs_ty.zigTypeTag(mod)) { | 1133 | _ = try self.addInst(.{ |
| 1066 | .Float => return self.fail("TODO binary operations on floats", .{}), | 1134 | .tag = mir_tag, |
| 1067 | .Vector => return self.fail("TODO binary operations on vectors", .{}), | 1135 | .data = .{ .i_type = .{ |
| 1068 | .Int => { | 1136 | .rd = dest_reg, |
| 1069 | assert(lhs_ty.eql(rhs_ty, mod)); | 1137 | .rs1 = lhs_reg, |
| 1070 | const int_info = lhs_ty.intInfo(mod); | 1138 | .imm12 = math.cast(i12, rhs.immediate) orelse { |
| 1071 | if (int_info.bits <= 64) { | 1139 | return self.fail("TODO: binOpImm larger than i12 i_type payload", .{}); |
| 1072 | if (rhs == .immediate) { | 1140 | }, |
| 1073 | return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | 1141 | } }, |
| 1074 | } | 1142 | }); |
| 1075 | return self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | | |
| 1076 | } else { | | |
| 1077 | return self.fail("TODO binary operations on int with bits > 64", .{}); | | |
| 1078 | } | | |
| 1079 | }, | | |
| 1080 | else => unreachable, | | |
| 1081 | } | | |
| 1082 | }, | | |
| 1083 | .ptr_add, | | |
| 1084 | .ptr_sub, | | |
| 1085 | => { | | |
| 1086 | switch (lhs_ty.zigTypeTag(mod)) { | | |
| 1087 | .Pointer => { | | |
| 1088 | const ptr_ty = lhs_ty; | | |
| 1089 | const elem_ty = switch (ptr_ty.ptrSize(mod)) { | | |
| 1090 | .One => ptr_ty.childType(mod).childType(mod), // ptr to array, so get array element type | | |
| 1091 | else => ptr_ty.childType(mod), | | |
| 1092 | }; | | |
| 1093 | const elem_size = elem_ty.abiSize(mod); | | |
| 1094 | | | |
| 1095 | if (elem_size == 1) { | | |
| 1096 | const base_tag: Air.Inst.Tag = switch (tag) { | | |
| 1097 | .ptr_add => .add, | | |
| 1098 | .ptr_sub => .sub, | | |
| 1099 | else => unreachable, | | |
| 1100 | }; | | |
| 1101 | | | |
| 1102 | return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | | |
| 1103 | } else { | | |
| 1104 | return self.fail("TODO ptr_add with elem_size > 1", .{}); | | |
| 1105 | } | | |
| 1106 | }, | | |
| 1107 | else => unreachable, | | |
| 1108 | } | | |
| 1109 | }, | 1143 | }, |
| | 1144 | .cmp_imm_gte => { |
| | 1145 | const imm_reg = try self.copyToTmpRegister(rhs_ty, .{ .immediate = rhs.immediate - 1 }); |
| 1110 | | 1146 | |
| 1111 | // These instructions have unsymteric bit sizes. | 1147 | _ = try self.addInst(.{ |
| 1112 | .shr, | 1148 | .tag = mir_tag, |
| 1113 | .shl, | 1149 | .data = .{ .r_type = .{ |
| 1114 | => { | 1150 | .rd = dest_reg, |
| 1115 | switch (lhs_ty.zigTypeTag(mod)) { | 1151 | .rs1 = imm_reg, |
| 1116 | .Float => return self.fail("TODO binary operations on floats", .{}), | 1152 | .rs2 = lhs_reg, |
| 1117 | .Vector => return self.fail("TODO binary operations on vectors", .{}), | 1153 | } }, |
| 1118 | .Int => { | 1154 | }); |
| 1119 | const int_info = lhs_ty.intInfo(mod); | | |
| 1120 | if (int_info.bits <= 64) { | | |
| 1121 | if (rhs == .immediate) { | | |
| 1122 | return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | | |
| 1123 | } | | |
| 1124 | return self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | | |
| 1125 | } else { | | |
| 1126 | return self.fail("TODO binary operations on int with bits > 64", .{}); | | |
| 1127 | } | | |
| 1128 | }, | | |
| 1129 | else => unreachable, | | |
| 1130 | } | | |
| 1131 | }, | 1155 | }, |
| 1132 | else => unreachable, | 1156 | else => unreachable, |
| 1133 | } | 1157 | } |
| 1134 | } | | |
| 1135 | | 1158 | |
| 1136 | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | 1159 | // generate the struct for overflow checks |
| 1137 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 1138 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 1139 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 1140 | const lhs_ty = self.typeOf(bin_op.lhs); | | |
| 1141 | const rhs_ty = self.typeOf(bin_op.rhs); | | |
| 1142 | | 1160 | |
| 1143 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); | 1161 | return MCValue{ .register = dest_reg }; |
| 1144 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1145 | } | 1162 | } |
| 1146 | | 1163 | |
| 1147 | fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | 1164 | fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| ... | @@ -2101,8 +2118,12 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2101,8 +2118,12 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2101 | const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]); | 2118 | const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]); |
| 2102 | const liveness_condbr = self.liveness.getCondBr(inst); | 2119 | const liveness_condbr = self.liveness.getCondBr(inst); |
| 2103 | | 2120 | |
| 2104 | // A branch to the false section. Uses beq | 2121 | const cond_reg = try self.register_manager.allocReg(inst, gp); |
| 2105 | const reloc = try self.condBr(cond_ty, cond); | 2122 | const cond_reg_lock = self.register_manager.lockRegAssumeUnused(cond_reg); |
| | 2123 | defer self.register_manager.unlockReg(cond_reg_lock); |
| | 2124 | |
| | 2125 | // A branch to the false section. Uses bne |
| | 2126 | const reloc = try self.condBr(cond_ty, cond, cond_reg); |
| 2106 | | 2127 | |
| 2107 | // If the condition dies here in this condbr instruction, process | 2128 | // If the condition dies here in this condbr instruction, process |
| 2108 | // that death now instead of later as this has an effect on | 2129 | // that death now instead of later as this has an effect on |
| ... | @@ -2233,19 +2254,14 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2233,19 +2254,14 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2233 | } | 2254 | } |
| 2234 | } | 2255 | } |
| 2235 | | 2256 | |
| 2236 | fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index { | 2257 | fn condBr(self: *Self, cond_ty: Type, condition: MCValue, cond_reg: Register) !Mir.Inst.Index { |
| 2237 | _ = cond_ty; | 2258 | try self.genSetReg(cond_ty, cond_reg, condition); |
| 2238 | | | |
| 2239 | const reg = switch (condition) { | | |
| 2240 | .register => |r| r, | | |
| 2241 | else => try self.copyToTmpRegister(Type.bool, condition), | | |
| 2242 | }; | | |
| 2243 | | 2259 | |
| 2244 | return try self.addInst(.{ | 2260 | return try self.addInst(.{ |
| 2245 | .tag = .bne, | 2261 | .tag = .bne, |
| 2246 | .data = .{ | 2262 | .data = .{ |
| 2247 | .b_type = .{ | 2263 | .b_type = .{ |
| 2248 | .rs1 = reg, | 2264 | .rs1 = cond_reg, |
| 2249 | .rs2 = .zero, | 2265 | .rs2 = .zero, |
| 2250 | .inst = undefined, | 2266 | .inst = undefined, |
| 2251 | }, | 2267 | }, |
| ... | @@ -2739,6 +2755,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner | ... | @@ -2739,6 +2755,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner |
| 2739 | } else return self.fail("TODO genSetStack for {s}", .{@tagName(self.bin_file.tag)}); | 2755 | } else return self.fail("TODO genSetStack for {s}", .{@tagName(self.bin_file.tag)}); |
| 2740 | }; | 2756 | }; |
| 2741 | | 2757 | |
| | 2758 | // setup the src pointer |
| 2742 | _ = try self.addInst(.{ | 2759 | _ = try self.addInst(.{ |
| 2743 | .tag = .load_symbol, | 2760 | .tag = .load_symbol, |
| 2744 | .data = .{ | 2761 | .data = .{ |
| ... | @@ -2789,7 +2806,7 @@ fn genInlineMemcpy( | ... | @@ -2789,7 +2806,7 @@ fn genInlineMemcpy( |
| 2789 | | 2806 | |
| 2790 | // compare count to length | 2807 | // compare count to length |
| 2791 | const compare_inst = try self.addInst(.{ | 2808 | const compare_inst = try self.addInst(.{ |
| 2792 | .tag = .cmp_gt, | 2809 | .tag = .cmp_eq, |
| 2793 | .data = .{ .r_type = .{ | 2810 | .data = .{ .r_type = .{ |
| 2794 | .rd = tmp, | 2811 | .rd = tmp, |
| 2795 | .rs1 = count, | 2812 | .rs1 = count, |
| ... | @@ -2861,9 +2878,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! | ... | @@ -2861,9 +2878,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! |
| 2861 | } }, | 2878 | } }, |
| 2862 | }); | 2879 | }); |
| 2863 | } else { | 2880 | } else { |
| | 2881 | // TODO: use a more advanced myriad seq to do this without a reg. |
| | 2882 | // see: https://github.com/llvm/llvm-project/blob/081a66ffacfe85a37ff775addafcf3371e967328/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.cpp#L224 |
| | 2883 | |
| 2864 | const temp = try self.register_manager.allocReg(null, gp); | 2884 | const temp = try self.register_manager.allocReg(null, gp); |
| 2865 | const maybe_temp_lock = self.register_manager.lockReg(temp); | 2885 | const temp_lock = self.register_manager.lockRegAssumeUnused(temp); |
| 2866 | defer if (maybe_temp_lock) |temp_lock| self.register_manager.unlockReg(temp_lock); | 2886 | defer self.register_manager.unlockReg(temp_lock); |
| 2867 | | 2887 | |
| 2868 | const lo32: i32 = @truncate(x); | 2888 | const lo32: i32 = @truncate(x); |
| 2869 | const carry: i32 = if (lo32 < 0) 1 else 0; | 2889 | const carry: i32 = if (lo32 < 0) 1 else 0; |