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 {...@@ -573,25 +573,54 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
573 // sub sp, sp, #reloc573 // sub sp, sp, #reloc
574 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.push(.al, .{ .fp, .lr }).toU32());574 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.push(.al, .{ .fp, .lr }).toU32());
575 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .fp, Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none)).toU32());575 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 variables576 const backpatch_reloc = self.code.items.len;
577 // const backpatch_reloc = try self.code.addManyAsArray(4);577 try self.code.resize(backpatch_reloc + 4);
578578
579 try self.dbgSetPrologueEnd();579 try self.dbgSetPrologueEnd();
580580
581 try self.genBody(self.mod_fn.analysis.success);581 try self.genBody(self.mod_fn.analysis.success);
582582
583 // Backpatch stack offset583 // Backpatch stack offset
584 // const stack_end = self.max_end_stack;584 const stack_end = self.max_end_stack;
585 // const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);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()));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
588 try self.dbgSetEpilogueBegin();592 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
590 // mov sp, fp620 // mov sp, fp
591 // pop {fp, pc}621 // pop {fp, pc}
592 // TODO: return by jumping to this code, use relocations622 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32());
593 // 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());
594 // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
595 } else {624 } else {
596 try self.dbgSetPrologueEnd();625 try self.dbgSetPrologueEnd();
597 try self.genBody(self.mod_fn.analysis.success);626 try self.genBody(self.mod_fn.analysis.success);
...@@ -1661,12 +1690,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1661,12 +1690,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1661 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());1690 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
1662 },1691 },
1663 .arm => {1692 .arm => {
1664 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32());1693 // Just add space for an instruction, patch this later
1665 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());1694 try self.code.resize(self.code.items.len + 4);
1666 // TODO: jump to the end with relocation1695 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
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);
1670 },1696 },
1671 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),1697 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
1672 }1698 }
...@@ -1932,6 +1958,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1932,6 +1958,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1932 mem.writeIntLittle(i32, self.code.addManyAsArrayAssumeCapacity(4), delta);1958 mem.writeIntLittle(i32, self.code.addManyAsArrayAssumeCapacity(4), delta);
1933 }1959 }
1934 },1960 },
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 },
1935 else => return self.fail(src, "TODO implement jump for {}", .{self.target.cpu.arch}),1968 else => return self.fail(src, "TODO implement jump for {}", .{self.target.cpu.arch}),
1936 }1969 }
1937 }1970 }
...@@ -2167,6 +2200,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2167,6 +2200,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21672200
2168 fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {2201 fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
2169 switch (arch) {2202 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 },
2170 .x86_64 => switch (mcv) {2255 .x86_64 => switch (mcv) {
2171 .dead => unreachable,2256 .dead => unreachable,
2172 .ptr_stack_offset => unreachable,2257 .ptr_stack_offset => unreachable,
...@@ -2274,35 +2359,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2274,35 +2359,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2274 return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa });2359 return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa });
2275 },2360 },
2276 .immediate => |x| {2361 .immediate => |x| {
2277 // TODO better analysis of x to determine the2362 if (x > math.maxInt(u32)) return self.fail(src, "ARM registers are 32-bit wide", .{});
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
22932363
2294 // immediate: 0xaabbccdd2364 if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| {
2295 // mov reg, #0xaa2365 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, op).toU32());
2296 // orr reg, reg, #0xbb, 242366 } else if (Instruction.Operand.fromU32(~@intCast(u32, x))) |op| {
2297 // orr reg, reg, #0xcc, 162367 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mvn(.al, reg, op).toU32());
2298 // orr reg, reg, #0xdd, 82368 } else if (x <= math.maxInt(u16)) {
2299 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());2369 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v7)) {
2300 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());2370 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movw(.al, reg, @intCast(u16, x)).toU32());
2301 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());2371 } else {
2302 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());2372 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2303 return;2373 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2374 }
2304 } else {2375 } 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 }
2306 }2395 }
2307 },2396 },
2308 .register => |src_reg| {2397 .register => |src_reg| {
...@@ -2319,6 +2408,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2319,6 +2408,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2319 try self.genSetReg(src, reg, .{ .immediate = addr });2408 try self.genSetReg(src, reg, .{ .immediate = addr });
2320 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());2409 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());
2321 },2410 },
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 },
2322 else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),2423 else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),
2323 },2424 },
2324 .riscv64 => switch (mcv) {2425 .riscv64 => switch (mcv) {
src/codegen/arm.zig+218-8
...@@ -138,6 +138,29 @@ pub const Instruction = union(enum) {...@@ -138,6 +138,29 @@ pub const Instruction = union(enum) {
138 fixed: u2 = 0b00,138 fixed: u2 = 0b00,
139 cond: u4,139 cond: u4,
140 },140 },
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 },
141 SingleDataTransfer: packed struct {164 SingleDataTransfer: packed struct {
142 offset: u12,165 offset: u12,
143 rd: u4,166 rd: u4,
...@@ -317,6 +340,29 @@ pub const Instruction = union(enum) {...@@ -317,6 +340,29 @@ pub const Instruction = union(enum) {
317 },340 },
318 };341 };
319 }342 }
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 }
320 };366 };
321367
322 /// Represents the offset operand of a load or store368 /// Represents the offset operand of a load or store
...@@ -349,7 +395,7 @@ pub const Instruction = union(enum) {...@@ -349,7 +395,7 @@ pub const Instruction = union(enum) {
349 };395 };
350 }396 }
351397
352 pub fn imm(immediate: u8) Offset {398 pub fn imm(immediate: u12) Offset {
353 return Offset{399 return Offset{
354 .Immediate = immediate,400 .Immediate = immediate,
355 };401 };
...@@ -380,6 +426,8 @@ pub const Instruction = union(enum) {...@@ -380,6 +426,8 @@ pub const Instruction = union(enum) {
380 pub fn toU32(self: Instruction) u32 {426 pub fn toU32(self: Instruction) u32 {
381 return switch (self) {427 return switch (self) {
382 .DataProcessing => |v| @bitCast(u32, v),428 .DataProcessing => |v| @bitCast(u32, v),
429 .Multiply => |v| @bitCast(u32, v),
430 .MultiplyLong => |v| @bitCast(u32, v),
383 .SingleDataTransfer => |v| @bitCast(u32, v),431 .SingleDataTransfer => |v| @bitCast(u32, v),
384 .BlockDataTransfer => |v| @bitCast(u32, v),432 .BlockDataTransfer => |v| @bitCast(u32, v),
385 .Branch => |v| @bitCast(u32, v),433 .Branch => |v| @bitCast(u32, v),
...@@ -412,6 +460,70 @@ pub const Instruction = union(enum) {...@@ -412,6 +460,70 @@ pub const Instruction = union(enum) {
412 };460 };
413 }461 }
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
415 fn singleDataTransfer(527 fn singleDataTransfer(
416 cond: Condition,528 cond: Condition,
417 rd: Register,529 rd: Register,
...@@ -463,12 +575,12 @@ pub const Instruction = union(enum) {...@@ -463,12 +575,12 @@ pub const Instruction = union(enum) {
463 };575 };
464 }576 }
465577
466 fn branch(cond: Condition, offset: i24, link: u1) Instruction {578 fn branch(cond: Condition, offset: i26, link: u1) Instruction {
467 return Instruction{579 return Instruction{
468 .Branch = .{580 .Branch = .{
469 .cond = @enumToInt(cond),581 .cond = @enumToInt(cond),
470 .link = link,582 .link = link,
471 .offset = @bitCast(u24, offset),583 .offset = @bitCast(u24, @intCast(i24, offset >> 2)),
472 },584 },
473 };585 };
474 }586 }
...@@ -618,10 +730,96 @@ pub const Instruction = union(enum) {...@@ -618,10 +730,96 @@ pub const Instruction = union(enum) {
618 return dataProcessing(cond, .mvn, 1, rd, .r0, op2);730 return dataProcessing(cond, .mvn, 1, rd, .r0, op2);
619 }731 }
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
621 // PSR transfer743 // PSR transfer
622744
623 pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction {745 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);
625 }823 }
626824
627 // Single data transfer825 // Single data transfer
...@@ -697,11 +895,11 @@ pub const Instruction = union(enum) {...@@ -697,11 +895,11 @@ pub const Instruction = union(enum) {
697895
698 // Branch896 // Branch
699897
700 pub fn b(cond: Condition, offset: i24) Instruction {898 pub fn b(cond: Condition, offset: i26) Instruction {
701 return branch(cond, offset, 0);899 return branch(cond, offset, 0);
702 }900 }
703901
704 pub fn bl(cond: Condition, offset: i24) Instruction {902 pub fn bl(cond: Condition, offset: i26) Instruction {
705 return branch(cond, offset, 1);903 return branch(cond, offset, 1);
706 }904 }
707905
...@@ -731,6 +929,10 @@ pub const Instruction = union(enum) {...@@ -731,6 +929,10 @@ pub const Instruction = union(enum) {
731929
732 // Aliases930 // Aliases
733931
932 pub fn nop() Instruction {
933 return mov(.al, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none));
934 }
935
734 pub fn pop(cond: Condition, args: anytype) Instruction {936 pub fn pop(cond: Condition, args: anytype) Instruction {
735 if (@typeInfo(@TypeOf(args)) != .Struct) {937 if (@typeInfo(@TypeOf(args)) != .Struct) {
736 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));938 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
...@@ -805,6 +1007,14 @@ test "serialize instructions" {...@@ -805,6 +1007,14 @@ test "serialize instructions" {
805 .inst = Instruction.mrs(.al, .r5, .cpsr),1007 .inst = Instruction.mrs(.al, .r5, .cpsr),
806 .expected = 0b1110_00010_0_001111_0101_000000000000,1008 .expected = 0b1110_00010_0_001111_0101_000000000000,
807 },1009 },
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 },
808 .{ // ldr r0, [r2, #42]1018 .{ // ldr r0, [r2, #42]
809 .inst = Instruction.ldr(.al, .r0, .r2, .{1019 .inst = Instruction.ldr(.al, .r0, .r2, .{
810 .offset = Instruction.Offset.imm(42),1020 .offset = Instruction.Offset.imm(42),
...@@ -819,11 +1029,11 @@ test "serialize instructions" {...@@ -819,11 +1029,11 @@ test "serialize instructions" {
819 },1029 },
820 .{ // b #121030 .{ // b #12
821 .inst = Instruction.b(.al, 12),1031 .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,
823 },1033 },
824 .{ // bl #-41034 .{ // bl #-4
825 .inst = Instruction.bl(.al, -4),1035 .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,
827 },1037 },
828 .{ // bx lr1038 .{ // bx lr
829 .inst = Instruction.bx(.al, .lr),1039 .inst = Instruction.bx(.al, .lr),