| ... | ... | @@ -850,6 +850,122 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 850 | 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 | 969 | /// Don't call this function directly. Use binOp instead. |
| 854 | 970 | /// |
| 855 | 971 | /// Calling this function signals an intention to generate a Mir |
| ... | ... | @@ -963,7 +1079,6 @@ fn binOpImm( |
| 963 | 1079 | lhs_ty: Type, |
| 964 | 1080 | rhs_ty: Type, |
| 965 | 1081 | ) !MCValue { |
| 966 | | _ = rhs_ty; |
| 967 | 1082 | assert(rhs == .immediate); |
| 968 | 1083 | |
| 969 | 1084 | const lhs_is_register = lhs == .register; |
| ... | ... | @@ -1006,142 +1121,44 @@ fn binOpImm( |
| 1006 | 1121 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1007 | 1122 | .shl => .slli, |
| 1008 | 1123 | .shr => .srli, |
| 1124 | .cmp_gte => .cmp_imm_gte, |
| 1009 | 1125 | else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}), |
| 1010 | 1126 | }; |
| 1011 | 1127 | |
| 1012 | | _ = try self.addInst(.{ |
| 1013 | | .tag = mir_tag, |
| 1014 | | .data = .{ |
| 1015 | | .i_type = .{ |
| 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, |
| 1128 | // apply some special operations needed |
| 1129 | switch (mir_tag) { |
| 1130 | .slli, |
| 1131 | .srli, |
| 1064 | 1132 | => { |
| 1065 | | switch (lhs_ty.zigTypeTag(mod)) { |
| 1066 | | .Float => return self.fail("TODO binary operations on floats", .{}), |
| 1067 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1068 | | .Int => { |
| 1069 | | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1070 | | const int_info = lhs_ty.intInfo(mod); |
| 1071 | | if (int_info.bits <= 64) { |
| 1072 | | if (rhs == .immediate) { |
| 1073 | | return self.binOpImm(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 1074 | | } |
| 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 | | } |
| 1133 | _ = try self.addInst(.{ |
| 1134 | .tag = mir_tag, |
| 1135 | .data = .{ .i_type = .{ |
| 1136 | .rd = dest_reg, |
| 1137 | .rs1 = lhs_reg, |
| 1138 | .imm12 = math.cast(i12, rhs.immediate) orelse { |
| 1139 | return self.fail("TODO: binOpImm larger than i12 i_type payload", .{}); |
| 1140 | }, |
| 1141 | } }, |
| 1142 | }); |
| 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. |
| 1112 | | .shr, |
| 1113 | | .shl, |
| 1114 | | => { |
| 1115 | | switch (lhs_ty.zigTypeTag(mod)) { |
| 1116 | | .Float => return self.fail("TODO binary operations on floats", .{}), |
| 1117 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1118 | | .Int => { |
| 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 | | } |
| 1147 | _ = try self.addInst(.{ |
| 1148 | .tag = mir_tag, |
| 1149 | .data = .{ .r_type = .{ |
| 1150 | .rd = dest_reg, |
| 1151 | .rs1 = imm_reg, |
| 1152 | .rs2 = lhs_reg, |
| 1153 | } }, |
| 1154 | }); |
| 1131 | 1155 | }, |
| 1132 | 1156 | else => unreachable, |
| 1133 | 1157 | } |
| 1134 | | } |
| 1135 | 1158 | |
| 1136 | | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 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); |
| 1159 | // generate the struct for overflow checks |
| 1142 | 1160 | |
| 1143 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); |
| 1144 | | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1161 | return MCValue{ .register = dest_reg }; |
| 1145 | 1162 | } |
| 1146 | 1163 | |
| 1147 | 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 | 2118 | const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]); |
| 2102 | 2119 | const liveness_condbr = self.liveness.getCondBr(inst); |
| 2103 | 2120 | |
| 2104 | | // A branch to the false section. Uses beq |
| 2105 | | const reloc = try self.condBr(cond_ty, cond); |
| 2121 | const cond_reg = try self.register_manager.allocReg(inst, gp); |
| 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 | 2128 | // If the condition dies here in this condbr instruction, process |
| 2108 | 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 | 2254 | } |
| 2234 | 2255 | } |
| 2235 | 2256 | |
| 2236 | | fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index { |
| 2237 | | _ = cond_ty; |
| 2238 | | |
| 2239 | | const reg = switch (condition) { |
| 2240 | | .register => |r| r, |
| 2241 | | else => try self.copyToTmpRegister(Type.bool, condition), |
| 2242 | | }; |
| 2257 | fn condBr(self: *Self, cond_ty: Type, condition: MCValue, cond_reg: Register) !Mir.Inst.Index { |
| 2258 | try self.genSetReg(cond_ty, cond_reg, condition); |
| 2243 | 2259 | |
| 2244 | 2260 | return try self.addInst(.{ |
| 2245 | 2261 | .tag = .bne, |
| 2246 | 2262 | .data = .{ |
| 2247 | 2263 | .b_type = .{ |
| 2248 | | .rs1 = reg, |
| 2264 | .rs1 = cond_reg, |
| 2249 | 2265 | .rs2 = .zero, |
| 2250 | 2266 | .inst = undefined, |
| 2251 | 2267 | }, |
| ... | ... | @@ -2739,6 +2755,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner |
| 2739 | 2755 | } else return self.fail("TODO genSetStack for {s}", .{@tagName(self.bin_file.tag)}); |
| 2740 | 2756 | }; |
| 2741 | 2757 | |
| 2758 | // setup the src pointer |
| 2742 | 2759 | _ = try self.addInst(.{ |
| 2743 | 2760 | .tag = .load_symbol, |
| 2744 | 2761 | .data = .{ |
| ... | ... | @@ -2789,7 +2806,7 @@ fn genInlineMemcpy( |
| 2789 | 2806 | |
| 2790 | 2807 | // compare count to length |
| 2791 | 2808 | const compare_inst = try self.addInst(.{ |
| 2792 | | .tag = .cmp_gt, |
| 2809 | .tag = .cmp_eq, |
| 2793 | 2810 | .data = .{ .r_type = .{ |
| 2794 | 2811 | .rd = tmp, |
| 2795 | 2812 | .rs1 = count, |
| ... | ... | @@ -2861,9 +2878,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! |
| 2861 | 2878 | } }, |
| 2862 | 2879 | }); |
| 2863 | 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 | 2884 | const temp = try self.register_manager.allocReg(null, gp); |
| 2865 | | const maybe_temp_lock = self.register_manager.lockReg(temp); |
| 2866 | | defer if (maybe_temp_lock) |temp_lock| self.register_manager.unlockReg(temp_lock); |
| 2885 | const temp_lock = self.register_manager.lockRegAssumeUnused(temp); |
| 2886 | defer self.register_manager.unlockReg(temp_lock); |
| 2867 | 2887 | |
| 2868 | 2888 | const lo32: i32 = @truncate(x); |
| 2869 | 2889 | const carry: i32 = if (lo32 < 0) 1 else 0; |