authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-17 00:09:33+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-17 15:07:42+01:00
log3193cc1c1ea1796df2ae40d5a11396d8626a8070
tree785a1ca2745450b15f484a55ba15bfed77c60b28
parentc1fb14c51ef9b329c0e4dfedfe6ae591bd972a88

x64: fix ptr_add and ptr_sub

Add standalone implementation of operand reuse for ptr related arithmetic operations of add and sub.

3 files changed, 52 insertions(+), 79 deletions(-)

src/arch/x86_64/CodeGen.zig+50-74
...@@ -1071,68 +1071,61 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void {...@@ -1071,68 +1071,61 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void {
1071 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1071 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1072}1072}
10731073
1074fn airPtrAdd(self: *Self, inst: Air.Inst.Index) !void {1074fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
1075 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1076 if (self.liveness.isUnused(inst)) {
1077 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1078 }
1079
1080 const mcvs = try self.mcvsForBinMathOp(inst, bin_op.lhs, bin_op.rhs);
1081 var dst_mcv = mcvs.dst;
1082 const src_mcv = mcvs.src;
1083
1084 // TODO clean this up
1085 // TODO take into account alignment
1086 const dst_ty = self.air.typeOfIndex(inst);1075 const dst_ty = self.air.typeOfIndex(inst);
1087 const elem_size = dst_ty.elemType2().abiSize(self.target.*);1076 const elem_size = dst_ty.elemType2().abiSize(self.target.*);
1088 const dst_reg = blk: {1077 const ptr = try self.resolveInst(op_lhs);
1089 switch (dst_mcv) {1078 const offset = try self.resolveInst(op_rhs);
1090 .register => |reg| break :blk reg,1079 const offset_ty = self.air.typeOf(op_rhs);
1091 else => {1080
1092 src_mcv.freezeIfRegister(&self.register_manager);1081 ptr.freezeIfRegister(&self.register_manager);
1093 defer src_mcv.freezeIfRegister(&self.register_manager);1082 defer ptr.unfreezeIfRegister(&self.register_manager);
1094 const reg = try self.copyToTmpRegister(dst_ty, dst_mcv);1083
1095 break :blk reg;1084 offset.freezeIfRegister(&self.register_manager);
1096 },1085 defer offset.unfreezeIfRegister(&self.register_manager);
1086
1087 const dst_mcv = blk: {
1088 if (self.reuseOperand(inst, op_lhs, 0, ptr)) {
1089 if (ptr.isMemory() or ptr.isRegister()) break :blk ptr;
1097 }1090 }
1091 break :blk try self.copyToNewRegister(inst, dst_ty, ptr);
1098 };1092 };
1099 try self.genIMulOpMir(dst_ty, .{ .register = dst_reg }, .{ .immediate = elem_size });
1100 dst_mcv = .{ .register = dst_reg };
1101 try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv);
11021093
1103 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });1094 const offset_mcv = blk: {
1104}1095 if (self.reuseOperand(inst, op_rhs, 1, offset)) {
1096 if (offset.isRegister()) break :blk offset;
1097 }
1098 break :blk MCValue{ .register = try self.copyToTmpRegister(offset_ty, offset) };
1099 };
11051100
1106fn airPtrSub(self: *Self, inst: Air.Inst.Index) !void {1101 try self.genIMulOpMir(offset_ty, offset_mcv, .{ .immediate = elem_size });
1107 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1102
1108 if (self.liveness.isUnused(inst)) {1103 const tag = self.air.instructions.items(.tag)[inst];
1109 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });1104 switch (tag) {
1105 .ptr_add => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, offset_mcv),
1106 .ptr_sub => try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, offset_mcv),
1107 else => unreachable,
1110 }1108 }
11111109
1112 const mcvs = try self.mcvsForBinMathOp(inst, bin_op.lhs, bin_op.rhs);1110 return dst_mcv;
1113 var dst_mcv = mcvs.dst;1111}
1114 const src_mcv = mcvs.src;
11151112
1116 // TODO clean this up1113fn airPtrAdd(self: *Self, inst: Air.Inst.Index) !void {
1117 // TODO take into account alignment1114 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1118 const dst_ty = self.air.typeOfIndex(inst);1115 const result = if (self.liveness.isUnused(inst))
1119 const elem_size = dst_ty.elemType2().abiSize(self.target.*);1116 .dead
1120 const dst_reg = blk: {1117 else
1121 switch (dst_mcv) {1118 try self.genPtrBinMathOp(inst, bin_op.lhs, bin_op.rhs);
1122 .register => |reg| break :blk reg,1119 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1123 else => {1120}
1124 src_mcv.freezeIfRegister(&self.register_manager);
1125 defer src_mcv.freezeIfRegister(&self.register_manager);
1126 const reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
1127 break :blk reg;
1128 },
1129 }
1130 };
1131 try self.genIMulOpMir(dst_ty, .{ .register = dst_reg }, .{ .immediate = elem_size });
1132 dst_mcv = .{ .register = dst_reg };
1133 try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, src_mcv);
11341121
1135 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });1122fn airPtrSub(self: *Self, inst: Air.Inst.Index) !void {
1123 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1124 const result = if (self.liveness.isUnused(inst))
1125 .dead
1126 else
1127 try self.genPtrBinMathOp(inst, bin_op.lhs, bin_op.rhs);
1128 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1136}1129}
11371130
1138fn airSlice(self: *Self, inst: Air.Inst.Index) !void {1131fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
...@@ -2178,17 +2171,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2178,17 +2171,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
2178 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });2171 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
2179}2172}
21802173
2181const BinMathOpMCValuePair = struct {2174/// Perform "binary" operators, excluding comparisons.
2182 dst: MCValue,2175/// Currently, the following ops are supported:
2183 src: MCValue,2176/// ADD, SUB, XOR, OR, AND
2184};2177fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
2185
2186fn mcvsForBinMathOp(
2187 self: *Self,
2188 inst: Air.Inst.Index,
2189 op_lhs: Air.Inst.Ref,
2190 op_rhs: Air.Inst.Ref,
2191) !BinMathOpMCValuePair {
2192 // TODO: make this algorithm less bad2178 // TODO: make this algorithm less bad
2193 const lhs = try self.resolveInst(op_lhs);2179 const lhs = try self.resolveInst(op_lhs);
2194 const rhs = try self.resolveInst(op_rhs);2180 const rhs = try self.resolveInst(op_rhs);
...@@ -2201,6 +2187,7 @@ fn mcvsForBinMathOp(...@@ -2201,6 +2187,7 @@ fn mcvsForBinMathOp(
2201 const dst_ty = self.air.typeOfIndex(inst);2187 const dst_ty = self.air.typeOfIndex(inst);
2202 var dst_mcv: MCValue = undefined;2188 var dst_mcv: MCValue = undefined;
2203 var src_mcv: MCValue = undefined;2189 var src_mcv: MCValue = undefined;
2190
2204 if (self.reuseOperand(inst, op_lhs, 0, lhs)) {2191 if (self.reuseOperand(inst, op_lhs, 0, lhs)) {
2205 // LHS dies; use it as the destination.2192 // LHS dies; use it as the destination.
2206 // Both operands cannot be memory.2193 // Both operands cannot be memory.
...@@ -2253,17 +2240,6 @@ fn mcvsForBinMathOp(...@@ -2253,17 +2240,6 @@ fn mcvsForBinMathOp(
2253 else => {},2240 else => {},
2254 }2241 }
22552242
2256 return BinMathOpMCValuePair{ .dst = dst_mcv, .src = src_mcv };
2257}
2258
2259/// Perform "binary" operators, excluding comparisons.
2260/// Currently, the following ops are supported:
2261/// ADD, SUB, XOR, OR, AND
2262fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
2263 const dst_ty = self.air.typeOfIndex(inst);
2264 const mcvs = try self.mcvsForBinMathOp(inst, op_lhs, op_rhs);
2265 const dst_mcv = mcvs.dst;
2266 const src_mcv = mcvs.src;
2267 const tag = self.air.instructions.items(.tag)[inst];2243 const tag = self.air.instructions.items(.tag)[inst];
2268 switch (tag) {2244 switch (tag) {
2269 .add, .addwrap => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv),2245 .add, .addwrap => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv),
test/behavior/align.zig-2
...@@ -106,7 +106,6 @@ fn fnWithAlignedStack() i32 {...@@ -106,7 +106,6 @@ fn fnWithAlignedStack() i32 {
106test "implicitly decreasing slice alignment" {106test "implicitly decreasing slice alignment" {
107 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;107 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
108 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;108 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
109 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
110109
111 const a: u32 align(4) = 3;110 const a: u32 align(4) = 3;
112 const b: u32 align(8) = 4;111 const b: u32 align(8) = 4;
...@@ -274,7 +273,6 @@ fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 {...@@ -274,7 +273,6 @@ fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 {
274test "runtime known array index has best alignment possible" {273test "runtime known array index has best alignment possible" {
275 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;274 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
276 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;275 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
277 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
278 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;276 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
279277
280 // take full advantage of over-alignment278 // take full advantage of over-alignment
test/behavior/cast.zig+2-3
...@@ -199,7 +199,7 @@ fn MakeType(comptime T: type) type {...@@ -199,7 +199,7 @@ fn MakeType(comptime T: type) type {
199199
200test "implicit cast from *[N]T to [*c]T" {200test "implicit cast from *[N]T to [*c]T" {
201 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;201 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
202 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;202 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
203203
204 var x: [4]u16 = [4]u16{ 0, 1, 2, 3 };204 var x: [4]u16 = [4]u16{ 0, 1, 2, 3 };
205 var y: [*c]u16 = &x;205 var y: [*c]u16 = &x;
...@@ -274,7 +274,7 @@ test "*const ?[*]const T to [*c]const [*c]const T" {...@@ -274,7 +274,7 @@ test "*const ?[*]const T to [*c]const [*c]const T" {
274274
275test "array coersion to undefined at runtime" {275test "array coersion to undefined at runtime" {
276 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;276 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
277 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;277 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
278278
279 @setRuntimeSafety(true);279 @setRuntimeSafety(true);
280280
...@@ -339,7 +339,6 @@ test "peer type unsigned int to signed" {...@@ -339,7 +339,6 @@ test "peer type unsigned int to signed" {
339test "expected [*c]const u8, found [*:0]const u8" {339test "expected [*c]const u8, found [*:0]const u8" {
340 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;340 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
341 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;341 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
342 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
343342
344 var a: [*:0]const u8 = "hello";343 var a: [*:0]const u8 = "hello";
345 var b: [*c]const u8 = a;344 var b: [*c]const u8 = a;