authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-29 18:29:24-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-10-29 18:29:24-04:00
logf4bb8be9fc8766fec93618f89552c28d3f0a201b
treece990f0559cdb2f10a315986ee1711b3d3964c15
parentaaff66b8edc549de79b997328ebad668838c5433
parent7b4f3c7cfce202117f62b12e9036e5c8ca6a0e56
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6654 from joachimschmidt557/stage2-arm

stage2 ARM: more stuff

2 files changed, 360 insertions(+), 49 deletions(-)

src/codegen.zig+142-41
......@@ -573,25 +573,54 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
573573 // sub sp, sp, #reloc
574574 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.push(.al, .{ .fp, .lr }).toU32());
575575 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .fp, Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none)).toU32());
576 // TODO: prepare stack for local variables
577 // const backpatch_reloc = try self.code.addManyAsArray(4);
576 const backpatch_reloc = self.code.items.len;
577 try self.code.resize(backpatch_reloc + 4);
578578
579579 try self.dbgSetPrologueEnd();
580580
581581 try self.genBody(self.mod_fn.analysis.success);
582582
583583 // Backpatch stack offset
584 // const stack_end = self.max_end_stack;
585 // const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
586 // mem.writeIntLittle(u32, backpatch_reloc, Instruction.sub(.al, .sp, .sp, Instruction.Operand.imm()));
584 const stack_end = self.max_end_stack;
585 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
586 if (Instruction.Operand.fromU32(@intCast(u32, aligned_stack_end))) |op| {
587 mem.writeIntLittle(u32, self.code.items[backpatch_reloc..][0..4], Instruction.sub(.al, .sp, .sp, op).toU32());
588 } else {
589 return self.fail(self.src, "TODO ARM: allow larger stacks", .{});
590 }
587591
588592 try self.dbgSetEpilogueBegin();
589593
594 // exitlude jumps
595 if (self.exitlude_jump_relocs.items.len == 1) {
596 // There is only one relocation. Hence,
597 // this relocation must be at the end of
598 // the code. Therefore, we can just delete
599 // the space initially reserved for the
600 // jump
601 self.code.items.len -= 4;
602 } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
603 const amt = self.code.items.len - (jmp_reloc + 4);
604 if (amt == 0) {
605 // This return is at the end of the
606 // code block. We can't just delete
607 // the space because there may be
608 // other jumps we already relocated to
609 // the address. Instead, insert a nop
610 mem.writeIntLittle(u32, self.code.items[jmp_reloc..][0..4], Instruction.nop().toU32());
611 } else {
612 if (math.cast(i26, amt)) |offset| {
613 mem.writeIntLittle(u32, self.code.items[jmp_reloc..][0..4], Instruction.b(.al, offset).toU32());
614 } else |err| {
615 return self.fail(self.src, "exitlude jump is too large", .{});
616 }
617 }
618 }
619
590620 // mov sp, fp
591621 // pop {fp, pc}
592 // TODO: return by jumping to this code, use relocations
593 // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32());
594 // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
622 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32());
623 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
595624 } else {
596625 try self.dbgSetPrologueEnd();
597626 try self.genBody(self.mod_fn.analysis.success);
......@@ -1661,12 +1690,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16611690 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
16621691 },
16631692 .arm => {
1664 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32());
1665 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
1666 // TODO: jump to the end with relocation
1667 // // Just add space for an instruction, patch this later
1668 // try self.code.resize(self.code.items.len + 4);
1669 // try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
1693 // Just add space for an instruction, patch this later
1694 try self.code.resize(self.code.items.len + 4);
1695 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
16701696 },
16711697 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
16721698 }
......@@ -1932,6 +1958,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19321958 mem.writeIntLittle(i32, self.code.addManyAsArrayAssumeCapacity(4), delta);
19331959 }
19341960 },
1961 .arm => {
1962 if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len))) |delta| {
1963 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(.al, delta).toU32());
1964 } else |err| {
1965 return self.fail(src, "TODO: enable larger branch offset", .{});
1966 }
1967 },
19351968 else => return self.fail(src, "TODO implement jump for {}", .{self.target.cpu.arch}),
19361969 }
19371970 }
......@@ -2167,6 +2200,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21672200
21682201 fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
21692202 switch (arch) {
2203 .arm => switch (mcv) {
2204 .dead => unreachable,
2205 .ptr_stack_offset => unreachable,
2206 .ptr_embedded_in_code => unreachable,
2207 .unreach, .none => return, // Nothing to do.
2208 .undef => {
2209 if (!self.wantSafety())
2210 return; // The already existing value will do just fine.
2211 // TODO Upgrade this to a memset call when we have that available.
2212 switch (ty.abiSize(self.target.*)) {
2213 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),
2214 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),
2215 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
2216 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
2217 else => return self.fail(src, "TODO implement memset", .{}),
2218 }
2219 },
2220 .compare_flags_unsigned => |op| {
2221 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});
2222 },
2223 .compare_flags_signed => |op| {
2224 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
2225 },
2226 .immediate => {
2227 const reg = try self.copyToTmpRegister(src, mcv);
2228 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
2229 },
2230 .embedded_in_code => |code_offset| {
2231 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
2232 },
2233 .register => |reg| {
2234 // TODO: strb, strh
2235 if (stack_offset <= math.maxInt(u12)) {
2236 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.al, reg, .fp, .{
2237 .offset = Instruction.Offset.imm(@intCast(u12, stack_offset)),
2238 .positive = false,
2239 }).toU32());
2240 } else {
2241 return self.fail(src, "TODO genSetStack with larger offsets", .{});
2242 }
2243 },
2244 .memory => |vaddr| {
2245 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
2246 },
2247 .stack_offset => |off| {
2248 if (stack_offset == off)
2249 return; // Copy stack variable to itself; nothing to do.
2250
2251 const reg = try self.copyToTmpRegister(src, mcv);
2252 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
2253 },
2254 },
21702255 .x86_64 => switch (mcv) {
21712256 .dead => unreachable,
21722257 .ptr_stack_offset => unreachable,
......@@ -2274,35 +2359,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22742359 return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa });
22752360 },
22762361 .immediate => |x| {
2277 // TODO better analysis of x to determine the
2278 // least amount of necessary instructions (use
2279 // more intelligent rotating)
2280 if (x <= math.maxInt(u8)) {
2281 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2282 return;
2283 } else if (x <= math.maxInt(u16)) {
2284 // TODO Use movw Note: Not supported on
2285 // all ARM targets!
2286 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2287 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2288 } else if (x <= math.maxInt(u32)) {
2289 // TODO Use movw and movt Note: Not
2290 // supported on all ARM targets! Also TODO
2291 // write constant to code and load
2292 // relative to pc
2362 if (x > math.maxInt(u32)) return self.fail(src, "ARM registers are 32-bit wide", .{});
22932363
2294 // immediate: 0xaabbccdd
2295 // mov reg, #0xaa
2296 // orr reg, reg, #0xbb, 24
2297 // orr reg, reg, #0xcc, 16
2298 // orr reg, reg, #0xdd, 8
2299 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2300 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2301 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
2302 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
2303 return;
2364 if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| {
2365 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, op).toU32());
2366 } else if (Instruction.Operand.fromU32(~@intCast(u32, x))) |op| {
2367 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mvn(.al, reg, op).toU32());
2368 } else if (x <= math.maxInt(u16)) {
2369 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v7)) {
2370 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movw(.al, reg, @intCast(u16, x)).toU32());
2371 } else {
2372 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2373 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2374 }
23042375 } else {
2305 return self.fail(src, "ARM registers are 32-bit wide", .{});
2376 // TODO write constant to code and load
2377 // relative to pc
2378 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v7)) {
2379 // immediate: 0xaaaabbbb
2380 // movw reg, #0xbbbb
2381 // movt reg, #0xaaaa
2382 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movw(.al, reg, @truncate(u16, x)).toU32());
2383 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movt(.al, reg, @truncate(u16, x >> 16)).toU32());
2384 } else {
2385 // immediate: 0xaabbccdd
2386 // mov reg, #0xaa
2387 // orr reg, reg, #0xbb, 24
2388 // orr reg, reg, #0xcc, 16
2389 // orr reg, reg, #0xdd, 8
2390 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2391 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2392 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
2393 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
2394 }
23062395 }
23072396 },
23082397 .register => |src_reg| {
......@@ -2319,6 +2408,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
23192408 try self.genSetReg(src, reg, .{ .immediate = addr });
23202409 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());
23212410 },
2411 .stack_offset => |unadjusted_off| {
2412 // TODO: ldrb, ldrh
2413 // TODO: maybe addressing from sp instead of fp
2414 if (unadjusted_off <= math.maxInt(u12)) {
2415 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, .fp, .{
2416 .offset = Instruction.Offset.imm(@intCast(u12, unadjusted_off)),
2417 .positive = false,
2418 }).toU32());
2419 } else {
2420 return self.fail(src, "TODO genSetReg with larger stack offset", .{});
2421 }
2422 },
23222423 else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),
23232424 },
23242425 .riscv64 => switch (mcv) {
src/codegen/arm.zig+218-8
......@@ -138,6 +138,29 @@ pub const Instruction = union(enum) {
138138 fixed: u2 = 0b00,
139139 cond: u4,
140140 },
141 Multiply: packed struct {
142 rn: u4,
143 fixed_1: u4 = 0b1001,
144 rm: u4,
145 ra: u4,
146 rd: u4,
147 set_cond: u1,
148 accumulate: u1,
149 fixed_2: u6 = 0b000000,
150 cond: u4,
151 },
152 MultiplyLong: packed struct {
153 rn: u4,
154 fixed_1: u4 = 0b1001,
155 rm: u4,
156 rdlo: u4,
157 rdhi: u4,
158 set_cond: u1,
159 accumulate: u1,
160 unsigned: u1,
161 fixed_2: u5 = 0b00001,
162 cond: u4,
163 },
141164 SingleDataTransfer: packed struct {
142165 offset: u12,
143166 rd: u4,
......@@ -317,6 +340,29 @@ pub const Instruction = union(enum) {
317340 },
318341 };
319342 }
343
344 /// Tries to convert an unsigned 32 bit integer into an
345 /// immediate operand using rotation. Returns null when there
346 /// is no conversion
347 pub fn fromU32(x: u32) ?Operand {
348 const masks = comptime blk: {
349 const base_mask: u32 = std.math.maxInt(u8);
350 var result = [_]u32{0} ** 16;
351 for (result) |*mask, i| mask.* = std.math.rotr(u32, base_mask, 2 * i);
352 break :blk result;
353 };
354
355 return for (masks) |mask, i| {
356 if (x & mask == x) {
357 break Operand{
358 .Immediate = .{
359 .imm = @intCast(u8, std.math.rotl(u32, x, 2 * i)),
360 .rotate = @intCast(u4, i),
361 },
362 };
363 }
364 } else null;
365 }
320366 };
321367
322368 /// Represents the offset operand of a load or store
......@@ -349,7 +395,7 @@ pub const Instruction = union(enum) {
349395 };
350396 }
351397
352 pub fn imm(immediate: u8) Offset {
398 pub fn imm(immediate: u12) Offset {
353399 return Offset{
354400 .Immediate = immediate,
355401 };
......@@ -380,6 +426,8 @@ pub const Instruction = union(enum) {
380426 pub fn toU32(self: Instruction) u32 {
381427 return switch (self) {
382428 .DataProcessing => |v| @bitCast(u32, v),
429 .Multiply => |v| @bitCast(u32, v),
430 .MultiplyLong => |v| @bitCast(u32, v),
383431 .SingleDataTransfer => |v| @bitCast(u32, v),
384432 .BlockDataTransfer => |v| @bitCast(u32, v),
385433 .Branch => |v| @bitCast(u32, v),
......@@ -412,6 +460,70 @@ pub const Instruction = union(enum) {
412460 };
413461 }
414462
463 fn specialMov(
464 cond: Condition,
465 rd: Register,
466 imm: u16,
467 top: bool,
468 ) Instruction {
469 return Instruction{
470 .DataProcessing = .{
471 .cond = @enumToInt(cond),
472 .i = 1,
473 .opcode = if (top) 0b1010 else 0b1000,
474 .s = 0,
475 .rn = @truncate(u4, imm >> 12),
476 .rd = rd.id(),
477 .op2 = @truncate(u12, imm),
478 },
479 };
480 }
481
482 fn multiply(
483 cond: Condition,
484 set_cond: u1,
485 rd: Register,
486 rn: Register,
487 rm: Register,
488 ra: ?Register,
489 ) Instruction {
490 return Instruction{
491 .Multiply = .{
492 .cond = @enumToInt(cond),
493 .accumulate = @boolToInt(ra != null),
494 .set_cond = set_cond,
495 .rd = rd.id(),
496 .rn = rn.id(),
497 .ra = if (ra) |reg| reg.id() else 0b0000,
498 .rm = rm.id(),
499 },
500 };
501 }
502
503 fn multiplyLong(
504 cond: Condition,
505 signed: u1,
506 accumulate: u1,
507 set_cond: u1,
508 rdhi: Register,
509 rdlo: Register,
510 rm: Register,
511 rn: Register,
512 ) Instruction {
513 return Instruction{
514 .MultiplyLong = .{
515 .cond = @enumToInt(cond),
516 .unsigned = signed,
517 .accumulate = accumulate,
518 .set_cond = set_cond,
519 .rdlo = rdlo.id(),
520 .rdhi = rdhi.id(),
521 .rn = rn.id(),
522 .rm = rm.id(),
523 },
524 };
525 }
526
415527 fn singleDataTransfer(
416528 cond: Condition,
417529 rd: Register,
......@@ -463,12 +575,12 @@ pub const Instruction = union(enum) {
463575 };
464576 }
465577
466 fn branch(cond: Condition, offset: i24, link: u1) Instruction {
578 fn branch(cond: Condition, offset: i26, link: u1) Instruction {
467579 return Instruction{
468580 .Branch = .{
469581 .cond = @enumToInt(cond),
470582 .link = link,
471 .offset = @bitCast(u24, offset),
583 .offset = @bitCast(u24, @intCast(i24, offset >> 2)),
472584 },
473585 };
474586 }
......@@ -618,10 +730,96 @@ pub const Instruction = union(enum) {
618730 return dataProcessing(cond, .mvn, 1, rd, .r0, op2);
619731 }
620732
733 // movw and movt
734
735 pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction {
736 return specialMov(cond, rd, imm, false);
737 }
738
739 pub fn movt(cond: Condition, rd: Register, imm: u16) Instruction {
740 return specialMov(cond, rd, imm, true);
741 }
742
621743 // PSR transfer
622744
623745 pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction {
624 return dataProcessing(cond, if (psr == .cpsr) .tst else .cmp, 0, rd, .r15, Operand.reg(.r0, Operand.Shift.none));
746 return Instruction{
747 .DataProcessing = .{
748 .cond = @enumToInt(cond),
749 .i = 0,
750 .opcode = if (psr == .spsr) 0b1010 else 0b1000,
751 .s = 0,
752 .rn = 0b1111,
753 .rd = rd.id(),
754 .op2 = 0b0000_0000_0000,
755 },
756 };
757 }
758
759 pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction {
760 return Instruction{
761 .DataProcessing = .{
762 .cond = @enumToInt(cond),
763 .i = 0,
764 .opcode = if (psr == .spsr) 0b1011 else 0b1001,
765 .s = 0,
766 .rn = 0b1111,
767 .rd = 0b1111,
768 .op2 = op.toU12(),
769 },
770 };
771 }
772
773 // Multiply
774
775 pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
776 return multiply(cond, 0, rd, rn, rm, null);
777 }
778
779 pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
780 return multiply(cond, 1, rd, rn, rm, null);
781 }
782
783 pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
784 return multiply(cond, 0, rd, rn, rm, ra);
785 }
786
787 pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
788 return multiply(cond, 1, rd, rn, rm, ra);
789 }
790
791 // Multiply long
792
793 pub fn umull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
794 return multiplyLong(cond, 0, 0, 0, rdhi, rdlo, rm, rn);
795 }
796
797 pub fn umulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
798 return multiplyLong(cond, 0, 0, 1, rdhi, rdlo, rm, rn);
799 }
800
801 pub fn umlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
802 return multiplyLong(cond, 0, 1, 0, rdhi, rdlo, rm, rn);
803 }
804
805 pub fn umlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
806 return multiplyLong(cond, 0, 1, 1, rdhi, rdlo, rm, rn);
807 }
808
809 pub fn smull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
810 return multiplyLong(cond, 1, 0, 0, rdhi, rdlo, rm, rn);
811 }
812
813 pub fn smulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
814 return multiplyLong(cond, 1, 0, 1, rdhi, rdlo, rm, rn);
815 }
816
817 pub fn smlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
818 return multiplyLong(cond, 1, 1, 0, rdhi, rdlo, rm, rn);
819 }
820
821 pub fn smlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
822 return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn);
625823 }
626824
627825 // Single data transfer
......@@ -697,11 +895,11 @@ pub const Instruction = union(enum) {
697895
698896 // Branch
699897
700 pub fn b(cond: Condition, offset: i24) Instruction {
898 pub fn b(cond: Condition, offset: i26) Instruction {
701899 return branch(cond, offset, 0);
702900 }
703901
704 pub fn bl(cond: Condition, offset: i24) Instruction {
902 pub fn bl(cond: Condition, offset: i26) Instruction {
705903 return branch(cond, offset, 1);
706904 }
707905
......@@ -731,6 +929,10 @@ pub const Instruction = union(enum) {
731929
732930 // Aliases
733931
932 pub fn nop() Instruction {
933 return mov(.al, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none));
934 }
935
734936 pub fn pop(cond: Condition, args: anytype) Instruction {
735937 if (@typeInfo(@TypeOf(args)) != .Struct) {
736938 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
......@@ -805,6 +1007,14 @@ test "serialize instructions" {
8051007 .inst = Instruction.mrs(.al, .r5, .cpsr),
8061008 .expected = 0b1110_00010_0_001111_0101_000000000000,
8071009 },
1010 .{ // mul r0, r1, r2
1011 .inst = Instruction.mul(.al, .r0, .r1, .r2),
1012 .expected = 0b1110_000000_0_0_0000_0000_0010_1001_0001,
1013 },
1014 .{ // umlal r0, r1, r5, r6
1015 .inst = Instruction.umlal(.al, .r0, .r1, .r5, .r6),
1016 .expected = 0b1110_00001_0_1_0_0001_0000_0110_1001_0101,
1017 },
8081018 .{ // ldr r0, [r2, #42]
8091019 .inst = Instruction.ldr(.al, .r0, .r2, .{
8101020 .offset = Instruction.Offset.imm(42),
......@@ -819,11 +1029,11 @@ test "serialize instructions" {
8191029 },
8201030 .{ // b #12
8211031 .inst = Instruction.b(.al, 12),
822 .expected = 0b1110_101_0_0000_0000_0000_0000_0000_1100,
1032 .expected = 0b1110_101_0_0000_0000_0000_0000_0000_0011,
8231033 },
8241034 .{ // bl #-4
8251035 .inst = Instruction.bl(.al, -4),
826 .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1100,
1036 .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1111,
8271037 },
8281038 .{ // bx lr
8291039 .inst = Instruction.bx(.al, .lr),