authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-02-03 20:31:01+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-04 21:07:10+01:00
log04f379dd414184a42412f4497b0573d7612d6730
treeb0cdc7bf12be59a5f28915485aaf7304c742dc15
parent71321b694195a87ab7394a25badf5295eb01875e

stage2 ARM: optimize airSliceElemVal for elem_size 1 or 4

In these cases, the AIR inst can be lowered to only one ldr instruction. Also fixes shifts in arm.bits.Offset

2 files changed, 139 insertions(+), 105 deletions(-)

src/arch/arm/CodeGen.zig+81-79
......@@ -1222,9 +1222,16 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
12221222fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
12231223 const is_volatile = false; // TODO
12241224 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1225 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else result: {
1225
1226 if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1227 const result: MCValue = result: {
12261228 const slice_mcv = try self.resolveInst(bin_op.lhs);
12271229
1230 // TODO optimize for the case where the index is a constant,
1231 // i.e. index_mcv == .immediate
1232 const index_mcv = try self.resolveInst(bin_op.rhs);
1233 const index_is_register = index_mcv == .register;
1234
12281235 const slice_ty = self.air.typeOf(bin_op.lhs);
12291236 const elem_ty = slice_ty.childType();
12301237 const elem_size = elem_ty.abiSize(self.target.*);
......@@ -1232,12 +1239,8 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
12321239 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
12331240 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
12341241
1235 // TODO optimize this for the case when elem_size is a power
1236 // of two (includes elem_size == 1)
1237 const offset_mcv = try self.genArmMulConstant(inst, bin_op.rhs, 1, @intCast(u32, elem_size));
1238 assert(offset_mcv == .register); // result of multiplication should always be register
1239 self.register_manager.freezeRegs(&.{offset_mcv.register});
1240 defer self.register_manager.unfreezeRegs(&.{offset_mcv.register});
1242 if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register});
1243 defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register});
12411244
12421245 const base_mcv: MCValue = switch (slice_mcv) {
12431246 .stack_offset => .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, slice_mcv) },
......@@ -1246,61 +1249,67 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
12461249 self.register_manager.freezeRegs(&.{base_mcv.register});
12471250 defer self.register_manager.unfreezeRegs(&.{base_mcv.register});
12481251
1249 if (elem_size <= 4) {
1250 const dst_reg = try self.register_manager.allocReg(inst);
1251 self.register_manager.freezeRegs(&.{dst_reg});
1252 defer self.register_manager.unfreezeRegs(&.{dst_reg});
1252 switch (elem_size) {
1253 1, 4 => {
1254 const dst_reg = try self.register_manager.allocReg(inst);
1255 const dst_mcv = MCValue{ .register = dst_reg };
1256 self.register_manager.freezeRegs(&.{dst_reg});
1257 defer self.register_manager.unfreezeRegs(&.{dst_reg});
12531258
1254 switch (elem_size) {
1255 1, 4 => {
1256 const tag: Mir.Inst.Tag = switch (elem_size) {
1257 1 => .ldrb,
1258 4 => .ldr,
1259 else => unreachable,
1260 };
1259 const index_reg: Register = switch (index_mcv) {
1260 .register => |reg| reg,
1261 else => try self.copyToTmpRegister(Type.usize, index_mcv),
1262 };
1263 self.register_manager.freezeRegs(&.{index_reg});
1264 defer self.register_manager.unfreezeRegs(&.{index_reg});
12611265
1262 _ = try self.addInst(.{
1263 .tag = tag,
1264 .data = .{ .rr_offset = .{
1265 .rt = dst_reg,
1266 .rn = base_mcv.register,
1267 .offset = .{ .offset = Instruction.Offset.reg(offset_mcv.register, 0) },
1268 } },
1269 });
1270 },
1271 2 => {
1272 _ = try self.addInst(.{
1273 .tag = .ldrh,
1274 .data = .{ .rr_extra_offset = .{
1275 .rt = dst_reg,
1276 .rn = base_mcv.register,
1277 .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.reg(offset_mcv.register) },
1278 } },
1279 });
1280 },
1281 else => unreachable,
1282 }
1266 const tag: Mir.Inst.Tag = switch (elem_size) {
1267 1 => .ldrb,
1268 4 => .ldr,
1269 else => unreachable,
1270 };
1271 const shift: u5 = switch (elem_size) {
1272 1 => 0,
1273 4 => 2,
1274 else => unreachable,
1275 };
12831276
1284 break :result MCValue{ .register = dst_reg };
1285 } else {
1286 const dst_mcv = try self.allocRegOrMem(inst, false);
1277 _ = try self.addInst(.{
1278 .tag = tag,
1279 .data = .{ .rr_offset = .{
1280 .rt = dst_reg,
1281 .rn = base_mcv.register,
1282 .offset = .{ .offset = Instruction.Offset.reg(index_reg, .{ .lsl = shift }) },
1283 } },
1284 });
12871285
1288 const addr_reg = try self.register_manager.allocReg(null);
1289 self.register_manager.freezeRegs(&.{addr_reg});
1290 defer self.register_manager.unfreezeRegs(&.{addr_reg});
1286 break :result dst_mcv;
1287 },
1288 else => {
1289 const dst_mcv = try self.allocRegOrMem(inst, true);
1290
1291 const offset_mcv = try self.genArmMulConstant(bin_op.rhs, @intCast(u32, elem_size));
1292 assert(offset_mcv == .register); // result of multiplication should always be register
1293 self.register_manager.freezeRegs(&.{offset_mcv.register});
1294 defer self.register_manager.unfreezeRegs(&.{offset_mcv.register});
12911295
1292 try self.genArmBinOpCode(addr_reg, base_mcv, offset_mcv, false, .add, .unsigned);
1296 const addr_reg = try self.register_manager.allocReg(null);
1297 self.register_manager.freezeRegs(&.{addr_reg});
1298 defer self.register_manager.unfreezeRegs(&.{addr_reg});
12931299
1294 // I know we will unfreeze these registers at the end of
1295 // the scope of :result. However, at this point in time,
1296 // neither the base register nor the offset register
1297 // contains any valuable data anymore. In order to reduce
1298 // register pressure, unfreeze them prematurely
1299 self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register });
1300 try self.genArmBinOpCode(addr_reg, base_mcv, offset_mcv, false, .add, .unsigned);
13001301
1301 try self.load(dst_mcv, .{ .register = addr_reg }, slice_ptr_field_type);
1302 // I know we will unfreeze these registers at the end of
1303 // the scope of :result. However, at this point in time,
1304 // neither the base register nor the offset register
1305 // contains any valuable data anymore. In order to reduce
1306 // register pressure, unfreeze them prematurely
1307 self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register });
13021308
1303 break :result dst_mcv;
1309 try self.load(dst_mcv, .{ .register = addr_reg }, slice_ptr_field_type);
1310
1311 break :result dst_mcv;
1312 },
13041313 }
13051314 };
13061315 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
......@@ -1931,8 +1940,8 @@ fn genArmBinOpCode(
19311940 .shl, .shr => {
19321941 assert(!swap_lhs_and_rhs);
19331942 const shift_amount = switch (operand) {
1934 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
1935 .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
1943 .register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
1944 .immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
19361945 };
19371946
19381947 const tag: Mir.Inst.Tag = switch (op) {
......@@ -2036,12 +2045,11 @@ fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Ai
20362045 return dst_mcv;
20372046}
20382047
2039fn genArmMulConstant(self: *Self, inst: Air.Inst.Index, op: Air.Inst.Ref, op_index: Liveness.OperandInt, imm: u32) !MCValue {
2048fn genArmMulConstant(self: *Self, op: Air.Inst.Ref, imm: u32) !MCValue {
20402049 const lhs = try self.resolveInst(op);
20412050 const rhs = MCValue{ .immediate = imm };
20422051
20432052 const lhs_is_register = lhs == .register;
2044 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op, op_index, lhs);
20452053
20462054 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
20472055 defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register});
......@@ -2054,23 +2062,17 @@ fn genArmMulConstant(self: *Self, inst: Air.Inst.Index, op: Air.Inst.Ref, op_ind
20542062 var rhs_mcv: MCValue = rhs;
20552063
20562064 // Allocate registers for operands and/or destination
2057 if (reuse_lhs) {
2058 // Allocate 1 register
2059 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(null) };
2060 dst_mcv = lhs;
2065 // Allocate 1 or 2 registers
2066 if (lhs_is_register) {
2067 // Move RHS to register
2068 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(null) };
2069 rhs_mcv = dst_mcv;
20612070 } else {
2062 // Allocate 1 or 2 registers
2063 if (lhs_is_register) {
2064 // Move RHS to register
2065 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(null) };
2066 rhs_mcv = dst_mcv;
2067 } else {
2068 // Move LHS and RHS to register
2069 const regs = try self.register_manager.allocRegs(2, .{ null, null });
2070 lhs_mcv = MCValue{ .register = regs[0] };
2071 rhs_mcv = MCValue{ .register = regs[1] };
2072 dst_mcv = lhs_mcv;
2073 }
2071 // Move LHS and RHS to register
2072 const regs = try self.register_manager.allocRegs(2, .{ null, null });
2073 lhs_mcv = MCValue{ .register = regs[0] };
2074 rhs_mcv = MCValue{ .register = regs[1] };
2075 dst_mcv = lhs_mcv;
20742076 }
20752077
20762078 // Move the operands to the newly allocated registers
......@@ -2132,7 +2134,7 @@ fn genArmInlineMemcpy(
21322134 .data = .{ .rr_offset = .{
21332135 .rt = tmp,
21342136 .rn = src,
2135 .offset = .{ .offset = Instruction.Offset.reg(count, 0) },
2137 .offset = .{ .offset = Instruction.Offset.reg(count, .none) },
21362138 } },
21372139 });
21382140
......@@ -2142,7 +2144,7 @@ fn genArmInlineMemcpy(
21422144 .data = .{ .rr_offset = .{
21432145 .rt = tmp,
21442146 .rn = dst,
2145 .offset = .{ .offset = Instruction.Offset.reg(count, 0) },
2147 .offset = .{ .offset = Instruction.Offset.reg(count, .none) },
21462148 } },
21472149 });
21482150
......@@ -3126,7 +3128,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
31263128 1, 4 => {
31273129 const offset = if (math.cast(u12, adj_off)) |imm| blk: {
31283130 break :blk Instruction.Offset.imm(imm);
3129 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
3131 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none);
31303132
31313133 const tag: Mir.Inst.Tag = switch (abi_size) {
31323134 1 => .strb,
......@@ -3450,7 +3452,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
34503452 1, 4 => {
34513453 const offset = if (adj_off <= math.maxInt(u12)) blk: {
34523454 break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
3453 } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
3455 } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none);
34543456
34553457 const tag: Mir.Inst.Tag = switch (abi_size) {
34563458 1 => .ldrb,
......@@ -3536,7 +3538,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
35363538 1, 4 => {
35373539 const offset = if (math.cast(u12, adj_off)) |imm| blk: {
35383540 break :blk Instruction.Offset.imm(imm);
3539 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
3541 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none);
35403542
35413543 const tag: Mir.Inst.Tag = switch (abi_size) {
35423544 1 => .strb,
src/arch/arm/bits.zig+58-26
......@@ -343,11 +343,11 @@ pub const Instruction = union(enum) {
343343 /// which can either be content from a register or an immediate
344344 /// value
345345 pub const Operand = union(enum) {
346 Register: packed struct {
346 register: packed struct {
347347 rm: u4,
348348 shift: u8,
349349 },
350 Immediate: packed struct {
350 immediate: packed struct {
351351 imm: u8,
352352 rotate: u4,
353353 },
......@@ -356,12 +356,12 @@ pub const Instruction = union(enum) {
356356 /// register can be shifted by a specific immediate value or
357357 /// by the contents of another register
358358 pub const Shift = union(enum) {
359 Immediate: packed struct {
359 immediate: packed struct {
360360 fixed: u1 = 0b0,
361361 typ: u2,
362362 amount: u5,
363363 },
364 Register: packed struct {
364 register: packed struct {
365365 fixed_1: u1 = 0b1,
366366 typ: u2,
367367 fixed_2: u1 = 0b0,
......@@ -376,7 +376,7 @@ pub const Instruction = union(enum) {
376376 };
377377
378378 pub const none = Shift{
379 .Immediate = .{
379 .immediate = .{
380380 .amount = 0,
381381 .typ = 0,
382382 },
......@@ -384,14 +384,14 @@ pub const Instruction = union(enum) {
384384
385385 pub fn toU8(self: Shift) u8 {
386386 return switch (self) {
387 .Register => |v| @bitCast(u8, v),
388 .Immediate => |v| @bitCast(u8, v),
387 .register => |v| @bitCast(u8, v),
388 .immediate => |v| @bitCast(u8, v),
389389 };
390390 }
391391
392392 pub fn reg(rs: Register, typ: Type) Shift {
393393 return Shift{
394 .Register = .{
394 .register = .{
395395 .rs = rs.id(),
396396 .typ = @enumToInt(typ),
397397 },
......@@ -400,7 +400,7 @@ pub const Instruction = union(enum) {
400400
401401 pub fn imm(amount: u5, typ: Type) Shift {
402402 return Shift{
403 .Immediate = .{
403 .immediate = .{
404404 .amount = amount,
405405 .typ = @enumToInt(typ),
406406 },
......@@ -410,14 +410,14 @@ pub const Instruction = union(enum) {
410410
411411 pub fn toU12(self: Operand) u12 {
412412 return switch (self) {
413 .Register => |v| @bitCast(u12, v),
414 .Immediate => |v| @bitCast(u12, v),
413 .register => |v| @bitCast(u12, v),
414 .immediate => |v| @bitCast(u12, v),
415415 };
416416 }
417417
418418 pub fn reg(rm: Register, shift: Shift) Operand {
419419 return Operand{
420 .Register = .{
420 .register = .{
421421 .rm = rm.id(),
422422 .shift = shift.toU8(),
423423 },
......@@ -426,7 +426,7 @@ pub const Instruction = union(enum) {
426426
427427 pub fn imm(immediate: u8, rotate: u4) Operand {
428428 return Operand{
429 .Immediate = .{
429 .immediate = .{
430430 .imm = immediate,
431431 .rotate = rotate,
432432 },
......@@ -447,7 +447,7 @@ pub const Instruction = union(enum) {
447447 return for (masks) |mask, i| {
448448 if (x & mask == x) {
449449 break Operand{
450 .Immediate = .{
450 .immediate = .{
451451 .imm = @intCast(u8, std.math.rotl(u32, x, 2 * i)),
452452 .rotate = @intCast(u4, i),
453453 },
......@@ -461,35 +461,67 @@ pub const Instruction = union(enum) {
461461 /// instruction. Data can be loaded from memory with either an
462462 /// immediate offset or an offset that is stored in some register.
463463 pub const Offset = union(enum) {
464 Immediate: u12,
465 Register: packed struct {
464 immediate: u12,
465 register: packed struct {
466466 rm: u4,
467 shift: u8,
467 fixed: u1 = 0b0,
468 stype: u2,
469 imm5: u5,
468470 },
469471
472 pub const Shift = union(enum) {
473 /// No shift
474 none,
475 /// Logical shift left
476 lsl: u5,
477 /// Logical shift right
478 lsr: u5,
479 /// Arithmetic shift right
480 asr: u5,
481 /// Rotate right
482 ror: u5,
483 /// Rotate right one bit, with extend
484 rrx,
485 };
486
470487 pub const none = Offset{
471 .Immediate = 0,
488 .immediate = 0,
472489 };
473490
474491 pub fn toU12(self: Offset) u12 {
475492 return switch (self) {
476 .Register => |v| @bitCast(u12, v),
477 .Immediate => |v| v,
493 .register => |v| @bitCast(u12, v),
494 .immediate => |v| v,
478495 };
479496 }
480497
481 pub fn reg(rm: Register, shift: u8) Offset {
498 pub fn reg(rm: Register, shift: Shift) Offset {
482499 return Offset{
483 .Register = .{
500 .register = .{
484501 .rm = rm.id(),
485 .shift = shift,
502 .stype = switch (shift) {
503 .none => 0b00,
504 .lsl => 0b00,
505 .lsr => 0b01,
506 .asr => 0b10,
507 .ror => 0b11,
508 .rrx => 0b11,
509 },
510 .imm5 = switch (shift) {
511 .none => 0,
512 .lsl => |n| n,
513 .lsr => |n| n,
514 .asr => |n| n,
515 .ror => |n| n,
516 .rrx => 0,
517 },
486518 },
487519 };
488520 }
489521
490522 pub fn imm(immediate: u12) Offset {
491523 return Offset{
492 .Immediate = immediate,
524 .immediate = immediate,
493525 };
494526 }
495527 };
......@@ -567,7 +599,7 @@ pub const Instruction = union(enum) {
567599 return Instruction{
568600 .data_processing = .{
569601 .cond = @enumToInt(cond),
570 .i = @boolToInt(op2 == .Immediate),
602 .i = @boolToInt(op2 == .immediate),
571603 .opcode = @enumToInt(opcode),
572604 .s = s,
573605 .rn = rn.id(),
......@@ -681,7 +713,7 @@ pub const Instruction = union(enum) {
681713 .byte_word = byte_word,
682714 .up_down = @boolToInt(positive),
683715 .pre_post = @boolToInt(pre_index),
684 .imm = @boolToInt(offset != .Immediate),
716 .imm = @boolToInt(offset != .immediate),
685717 },
686718 };
687719 }