aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/arm.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-09-30 21:38:04 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-09-30 21:38:04 -0700
commit3eb729b442d6cc69bd9a9d37816e9458190bc30b (patch)
treea412281c18914503c1566e79ab6a6f4eea187bf3 /src/codegen/arm.zig
parent1f653b7f8e9e358a5bfe2695a11c01da56f3d5ee (diff)
parentc4cd592f0e1eeff5a4056796610d97010ae4e38c (diff)
downloadzig-3eb729b442d6cc69bd9a9d37816e9458190bc30b.tar.gz
zig-3eb729b442d6cc69bd9a9d37816e9458190bc30b.zip
Merge remote-tracking branch 'origin/master' into llvm13
Diffstat (limited to 'src/codegen/arm.zig')
-rw-r--r--src/codegen/arm.zig1408
1 files changed, 0 insertions, 1408 deletions
diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig
deleted file mode 100644
index ec9152f96b..0000000000
--- a/src/codegen/arm.zig
+++ /dev/null
@@ -1,1408 +0,0 @@
-const std = @import("std");
-const DW = std.dwarf;
-const testing = std.testing;
-
-/// The condition field specifies the flags neccessary for an
-/// Instruction to be executed
-pub const Condition = enum(u4) {
- /// equal
- eq,
- /// not equal
- ne,
- /// unsigned higher or same
- cs,
- /// unsigned lower
- cc,
- /// negative
- mi,
- /// positive or zero
- pl,
- /// overflow
- vs,
- /// no overflow
- vc,
- /// unsigned higer
- hi,
- /// unsigned lower or same
- ls,
- /// greater or equal
- ge,
- /// less than
- lt,
- /// greater than
- gt,
- /// less than or equal
- le,
- /// always
- al,
-
- /// Converts a std.math.CompareOperator into a condition flag,
- /// i.e. returns the condition that is true iff the result of the
- /// comparison is true. Assumes signed comparison
- pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition {
- return switch (op) {
- .gte => .ge,
- .gt => .gt,
- .neq => .ne,
- .lt => .lt,
- .lte => .le,
- .eq => .eq,
- };
- }
-
- /// Converts a std.math.CompareOperator into a condition flag,
- /// i.e. returns the condition that is true iff the result of the
- /// comparison is true. Assumes unsigned comparison
- pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition {
- return switch (op) {
- .gte => .cs,
- .gt => .hi,
- .neq => .ne,
- .lt => .cc,
- .lte => .ls,
- .eq => .eq,
- };
- }
-
- /// Returns the condition which is true iff the given condition is
- /// false (if such a condition exists)
- pub fn negate(cond: Condition) Condition {
- return switch (cond) {
- .eq => .ne,
- .ne => .eq,
- .cs => .cc,
- .cc => .cs,
- .mi => .pl,
- .pl => .mi,
- .vs => .vc,
- .vc => .vs,
- .hi => .ls,
- .ls => .hi,
- .ge => .lt,
- .lt => .ge,
- .gt => .le,
- .le => .gt,
- .al => unreachable,
- };
- }
-};
-
-test "condition from CompareOperator" {
- try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorSigned(.eq));
- try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorUnsigned(.eq));
-
- try testing.expectEqual(@as(Condition, .gt), Condition.fromCompareOperatorSigned(.gt));
- try testing.expectEqual(@as(Condition, .hi), Condition.fromCompareOperatorUnsigned(.gt));
-
- try testing.expectEqual(@as(Condition, .le), Condition.fromCompareOperatorSigned(.lte));
- try testing.expectEqual(@as(Condition, .ls), Condition.fromCompareOperatorUnsigned(.lte));
-}
-
-test "negate condition" {
- try testing.expectEqual(@as(Condition, .eq), Condition.ne.negate());
- try testing.expectEqual(@as(Condition, .ne), Condition.eq.negate());
-}
-
-/// Represents a register in the ARM instruction set architecture
-pub const Register = enum(u5) {
- r0,
- r1,
- r2,
- r3,
- r4,
- r5,
- r6,
- r7,
- r8,
- r9,
- r10,
- r11,
- r12,
- r13,
- r14,
- r15,
-
- /// Argument / result / scratch register 1
- a1,
- /// Argument / result / scratch register 2
- a2,
- /// Argument / scratch register 3
- a3,
- /// Argument / scratch register 4
- a4,
- /// Variable-register 1
- v1,
- /// Variable-register 2
- v2,
- /// Variable-register 3
- v3,
- /// Variable-register 4
- v4,
- /// Variable-register 5
- v5,
- /// Platform register
- v6,
- /// Variable-register 7
- v7,
- /// Frame pointer or Variable-register 8
- fp,
- /// Intra-Procedure-call scratch register
- ip,
- /// Stack pointer
- sp,
- /// Link register
- lr,
- /// Program counter
- pc,
-
- /// Returns the unique 4-bit ID of this register which is used in
- /// the machine code
- pub fn id(self: Register) u4 {
- return @truncate(u4, @enumToInt(self));
- }
-
- /// Returns the index into `callee_preserved_regs`.
- pub fn allocIndex(self: Register) ?u4 {
- inline for (callee_preserved_regs) |cpreg, i| {
- if (self.id() == cpreg.id()) return i;
- }
- return null;
- }
-
- pub fn dwarfLocOp(self: Register) u8 {
- return @as(u8, self.id()) + DW.OP.reg0;
- }
-};
-
-test "Register.id" {
- try testing.expectEqual(@as(u4, 15), Register.r15.id());
- try testing.expectEqual(@as(u4, 15), Register.pc.id());
-}
-
-/// Program status registers containing flags, mode bits and other
-/// vital information
-pub const Psr = enum {
- cpsr,
- spsr,
-};
-
-pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 };
-pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
-pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
-
-/// Represents an instruction in the ARM instruction set architecture
-pub const Instruction = union(enum) {
- data_processing: packed struct {
- // Note to self: The order of the fields top-to-bottom is
- // right-to-left in the actual 32-bit int representation
- op2: u12,
- rd: u4,
- rn: u4,
- s: u1,
- opcode: u4,
- i: u1,
- fixed: u2 = 0b00,
- cond: u4,
- },
- multiply: packed struct {
- rn: u4,
- fixed_1: u4 = 0b1001,
- rm: u4,
- ra: u4,
- rd: u4,
- set_cond: u1,
- accumulate: u1,
- fixed_2: u6 = 0b000000,
- cond: u4,
- },
- multiply_long: packed struct {
- rn: u4,
- fixed_1: u4 = 0b1001,
- rm: u4,
- rdlo: u4,
- rdhi: u4,
- set_cond: u1,
- accumulate: u1,
- unsigned: u1,
- fixed_2: u5 = 0b00001,
- cond: u4,
- },
- integer_saturating_arithmetic: packed struct {
- rm: u4,
- fixed_1: u8 = 0b0000_0101,
- rd: u4,
- rn: u4,
- fixed_2: u1 = 0b0,
- opc: u2,
- fixed_3: u5 = 0b00010,
- cond: u4,
- },
- single_data_transfer: packed struct {
- offset: u12,
- rd: u4,
- rn: u4,
- load_store: u1,
- write_back: u1,
- byte_word: u1,
- up_down: u1,
- pre_post: u1,
- imm: u1,
- fixed: u2 = 0b01,
- cond: u4,
- },
- extra_load_store: packed struct {
- imm4l: u4,
- fixed_1: u1 = 0b1,
- op2: u2,
- fixed_2: u1 = 0b1,
- imm4h: u4,
- rt: u4,
- rn: u4,
- o1: u1,
- write_back: u1,
- imm: u1,
- up_down: u1,
- pre_index: u1,
- fixed_3: u3 = 0b000,
- cond: u4,
- },
- block_data_transfer: packed struct {
- register_list: u16,
- rn: u4,
- load_store: u1,
- write_back: u1,
- psr_or_user: u1,
- up_down: u1,
- pre_post: u1,
- fixed: u3 = 0b100,
- cond: u4,
- },
- branch: packed struct {
- offset: u24,
- link: u1,
- fixed: u3 = 0b101,
- cond: u4,
- },
- branch_exchange: packed struct {
- rn: u4,
- fixed_1: u1 = 0b1,
- link: u1,
- fixed_2: u22 = 0b0001_0010_1111_1111_1111_00,
- cond: u4,
- },
- supervisor_call: packed struct {
- comment: u24,
- fixed: u4 = 0b1111,
- cond: u4,
- },
- breakpoint: packed struct {
- imm4: u4,
- fixed_1: u4 = 0b0111,
- imm12: u12,
- fixed_2_and_cond: u12 = 0b1110_0001_0010,
- },
-
- /// Represents the possible operations which can be performed by a
- /// Data Processing instruction
- const Opcode = enum(u4) {
- // Rd := Op1 AND Op2
- @"and",
- // Rd := Op1 EOR Op2
- eor,
- // Rd := Op1 - Op2
- sub,
- // Rd := Op2 - Op1
- rsb,
- // Rd := Op1 + Op2
- add,
- // Rd := Op1 + Op2 + C
- adc,
- // Rd := Op1 - Op2 + C - 1
- sbc,
- // Rd := Op2 - Op1 + C - 1
- rsc,
- // set condition codes on Op1 AND Op2
- tst,
- // set condition codes on Op1 EOR Op2
- teq,
- // set condition codes on Op1 - Op2
- cmp,
- // set condition codes on Op1 + Op2
- cmn,
- // Rd := Op1 OR Op2
- orr,
- // Rd := Op2
- mov,
- // Rd := Op1 AND NOT Op2
- bic,
- // Rd := NOT Op2
- mvn,
- };
-
- /// Represents the second operand to a data processing instruction
- /// which can either be content from a register or an immediate
- /// value
- pub const Operand = union(enum) {
- Register: packed struct {
- rm: u4,
- shift: u8,
- },
- Immediate: packed struct {
- imm: u8,
- rotate: u4,
- },
-
- /// Represents multiple ways a register can be shifted. A
- /// register can be shifted by a specific immediate value or
- /// by the contents of another register
- pub const Shift = union(enum) {
- Immediate: packed struct {
- fixed: u1 = 0b0,
- typ: u2,
- amount: u5,
- },
- Register: packed struct {
- fixed_1: u1 = 0b1,
- typ: u2,
- fixed_2: u1 = 0b0,
- rs: u4,
- },
-
- pub const Type = enum(u2) {
- logical_left,
- logical_right,
- arithmetic_right,
- rotate_right,
- };
-
- pub const none = Shift{
- .Immediate = .{
- .amount = 0,
- .typ = 0,
- },
- };
-
- pub fn toU8(self: Shift) u8 {
- return switch (self) {
- .Register => |v| @bitCast(u8, v),
- .Immediate => |v| @bitCast(u8, v),
- };
- }
-
- pub fn reg(rs: Register, typ: Type) Shift {
- return Shift{
- .Register = .{
- .rs = rs.id(),
- .typ = @enumToInt(typ),
- },
- };
- }
-
- pub fn imm(amount: u5, typ: Type) Shift {
- return Shift{
- .Immediate = .{
- .amount = amount,
- .typ = @enumToInt(typ),
- },
- };
- }
- };
-
- pub fn toU12(self: Operand) u12 {
- return switch (self) {
- .Register => |v| @bitCast(u12, v),
- .Immediate => |v| @bitCast(u12, v),
- };
- }
-
- pub fn reg(rm: Register, shift: Shift) Operand {
- return Operand{
- .Register = .{
- .rm = rm.id(),
- .shift = shift.toU8(),
- },
- };
- }
-
- pub fn imm(immediate: u8, rotate: u4) Operand {
- return Operand{
- .Immediate = .{
- .imm = immediate,
- .rotate = rotate,
- },
- };
- }
-
- /// Tries to convert an unsigned 32 bit integer into an
- /// immediate operand using rotation. Returns null when there
- /// is no conversion
- pub fn fromU32(x: u32) ?Operand {
- const masks = comptime blk: {
- const base_mask: u32 = std.math.maxInt(u8);
- var result = [_]u32{0} ** 16;
- for (result) |*mask, i| mask.* = std.math.rotr(u32, base_mask, 2 * i);
- break :blk result;
- };
-
- return for (masks) |mask, i| {
- if (x & mask == x) {
- break Operand{
- .Immediate = .{
- .imm = @intCast(u8, std.math.rotl(u32, x, 2 * i)),
- .rotate = @intCast(u4, i),
- },
- };
- }
- } else null;
- }
- };
-
- /// Represents the offset operand of a load or store
- /// instruction. Data can be loaded from memory with either an
- /// immediate offset or an offset that is stored in some register.
- pub const Offset = union(enum) {
- Immediate: u12,
- Register: packed struct {
- rm: u4,
- shift: u8,
- },
-
- pub const none = Offset{
- .Immediate = 0,
- };
-
- pub fn toU12(self: Offset) u12 {
- return switch (self) {
- .Register => |v| @bitCast(u12, v),
- .Immediate => |v| v,
- };
- }
-
- pub fn reg(rm: Register, shift: u8) Offset {
- return Offset{
- .Register = .{
- .rm = rm.id(),
- .shift = shift,
- },
- };
- }
-
- pub fn imm(immediate: u12) Offset {
- return Offset{
- .Immediate = immediate,
- };
- }
- };
-
- /// Represents the offset operand of an extra load or store
- /// instruction.
- pub const ExtraLoadStoreOffset = union(enum) {
- immediate: u8,
- register: u4,
-
- pub const none = ExtraLoadStoreOffset{
- .immediate = 0,
- };
-
- pub fn reg(register: Register) ExtraLoadStoreOffset {
- return ExtraLoadStoreOffset{
- .register = register.id(),
- };
- }
-
- pub fn imm(immediate: u8) ExtraLoadStoreOffset {
- return ExtraLoadStoreOffset{
- .immediate = immediate,
- };
- }
- };
-
- /// Represents the register list operand to a block data transfer
- /// instruction
- pub const RegisterList = packed struct {
- r0: bool = false,
- r1: bool = false,
- r2: bool = false,
- r3: bool = false,
- r4: bool = false,
- r5: bool = false,
- r6: bool = false,
- r7: bool = false,
- r8: bool = false,
- r9: bool = false,
- r10: bool = false,
- r11: bool = false,
- r12: bool = false,
- r13: bool = false,
- r14: bool = false,
- r15: bool = false,
- };
-
- pub fn toU32(self: Instruction) u32 {
- return switch (self) {
- .data_processing => |v| @bitCast(u32, v),
- .multiply => |v| @bitCast(u32, v),
- .multiply_long => |v| @bitCast(u32, v),
- .integer_saturating_arithmetic => |v| @bitCast(u32, v),
- .single_data_transfer => |v| @bitCast(u32, v),
- .extra_load_store => |v| @bitCast(u32, v),
- .block_data_transfer => |v| @bitCast(u32, v),
- .branch => |v| @bitCast(u32, v),
- .branch_exchange => |v| @bitCast(u32, v),
- .supervisor_call => |v| @bitCast(u32, v),
- .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20),
- };
- }
-
- // Helper functions for the "real" functions below
-
- fn dataProcessing(
- cond: Condition,
- opcode: Opcode,
- s: u1,
- rd: Register,
- rn: Register,
- op2: Operand,
- ) Instruction {
- return Instruction{
- .data_processing = .{
- .cond = @enumToInt(cond),
- .i = @boolToInt(op2 == .Immediate),
- .opcode = @enumToInt(opcode),
- .s = s,
- .rn = rn.id(),
- .rd = rd.id(),
- .op2 = op2.toU12(),
- },
- };
- }
-
- fn specialMov(
- cond: Condition,
- rd: Register,
- imm: u16,
- top: bool,
- ) Instruction {
- return Instruction{
- .data_processing = .{
- .cond = @enumToInt(cond),
- .i = 1,
- .opcode = if (top) 0b1010 else 0b1000,
- .s = 0,
- .rn = @truncate(u4, imm >> 12),
- .rd = rd.id(),
- .op2 = @truncate(u12, imm),
- },
- };
- }
-
- fn multiply(
- cond: Condition,
- set_cond: u1,
- rd: Register,
- rn: Register,
- rm: Register,
- ra: ?Register,
- ) Instruction {
- return Instruction{
- .multiply = .{
- .cond = @enumToInt(cond),
- .accumulate = @boolToInt(ra != null),
- .set_cond = set_cond,
- .rd = rd.id(),
- .rn = rn.id(),
- .ra = if (ra) |reg| reg.id() else 0b0000,
- .rm = rm.id(),
- },
- };
- }
-
- fn multiplyLong(
- cond: Condition,
- signed: u1,
- accumulate: u1,
- set_cond: u1,
- rdhi: Register,
- rdlo: Register,
- rm: Register,
- rn: Register,
- ) Instruction {
- return Instruction{
- .multiply_long = .{
- .cond = @enumToInt(cond),
- .unsigned = signed,
- .accumulate = accumulate,
- .set_cond = set_cond,
- .rdlo = rdlo.id(),
- .rdhi = rdhi.id(),
- .rn = rn.id(),
- .rm = rm.id(),
- },
- };
- }
-
- fn integerSaturationArithmetic(
- cond: Condition,
- rd: Register,
- rm: Register,
- rn: Register,
- opc: u2,
- ) Instruction {
- return Instruction{
- .integer_saturating_arithmetic = .{
- .rm = rm.id(),
- .rd = rd.id(),
- .rn = rn.id(),
- .opc = opc,
- .cond = @enumToInt(cond),
- },
- };
- }
-
- fn singleDataTransfer(
- cond: Condition,
- rd: Register,
- rn: Register,
- offset: Offset,
- pre_index: bool,
- positive: bool,
- byte_word: u1,
- write_back: bool,
- load_store: u1,
- ) Instruction {
- return Instruction{
- .single_data_transfer = .{
- .cond = @enumToInt(cond),
- .rn = rn.id(),
- .rd = rd.id(),
- .offset = offset.toU12(),
- .load_store = load_store,
- .write_back = @boolToInt(write_back),
- .byte_word = byte_word,
- .up_down = @boolToInt(positive),
- .pre_post = @boolToInt(pre_index),
- .imm = @boolToInt(offset != .Immediate),
- },
- };
- }
-
- fn extraLoadStore(
- cond: Condition,
- pre_index: bool,
- positive: bool,
- write_back: bool,
- o1: u1,
- op2: u2,
- rn: Register,
- rt: Register,
- offset: ExtraLoadStoreOffset,
- ) Instruction {
- const imm4l: u4 = switch (offset) {
- .immediate => |imm| @truncate(u4, imm),
- .register => |reg| reg,
- };
- const imm4h: u4 = switch (offset) {
- .immediate => |imm| @truncate(u4, imm >> 4),
- .register => 0b0000,
- };
-
- return Instruction{
- .extra_load_store = .{
- .imm4l = imm4l,
- .op2 = op2,
- .imm4h = imm4h,
- .rt = rt.id(),
- .rn = rn.id(),
- .o1 = o1,
- .write_back = @boolToInt(write_back),
- .imm = @boolToInt(offset == .immediate),
- .up_down = @boolToInt(positive),
- .pre_index = @boolToInt(pre_index),
- .cond = @enumToInt(cond),
- },
- };
- }
-
- fn blockDataTransfer(
- cond: Condition,
- rn: Register,
- reg_list: RegisterList,
- pre_post: u1,
- up_down: u1,
- psr_or_user: u1,
- write_back: bool,
- load_store: u1,
- ) Instruction {
- return Instruction{
- .block_data_transfer = .{
- .register_list = @bitCast(u16, reg_list),
- .rn = rn.id(),
- .load_store = load_store,
- .write_back = @boolToInt(write_back),
- .psr_or_user = psr_or_user,
- .up_down = up_down,
- .pre_post = pre_post,
- .cond = @enumToInt(cond),
- },
- };
- }
-
- fn branch(cond: Condition, offset: i26, link: u1) Instruction {
- return Instruction{
- .branch = .{
- .cond = @enumToInt(cond),
- .link = link,
- .offset = @bitCast(u24, @intCast(i24, offset >> 2)),
- },
- };
- }
-
- fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction {
- return Instruction{
- .branch_exchange = .{
- .cond = @enumToInt(cond),
- .link = link,
- .rn = rn.id(),
- },
- };
- }
-
- fn supervisorCall(cond: Condition, comment: u24) Instruction {
- return Instruction{
- .supervisor_call = .{
- .cond = @enumToInt(cond),
- .comment = comment,
- },
- };
- }
-
- fn breakpoint(imm: u16) Instruction {
- return Instruction{
- .breakpoint = .{
- .imm12 = @truncate(u12, imm >> 4),
- .imm4 = @truncate(u4, imm),
- },
- };
- }
-
- // Public functions replicating assembler syntax as closely as
- // possible
-
- // Data processing
-
- pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .@"and", 0, rd, rn, op2);
- }
-
- pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .@"and", 1, rd, rn, op2);
- }
-
- pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .eor, 0, rd, rn, op2);
- }
-
- pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .eor, 1, rd, rn, op2);
- }
-
- pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .sub, 0, rd, rn, op2);
- }
-
- pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .sub, 1, rd, rn, op2);
- }
-
- pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .rsb, 0, rd, rn, op2);
- }
-
- pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .rsb, 1, rd, rn, op2);
- }
-
- pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .add, 0, rd, rn, op2);
- }
-
- pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .add, 1, rd, rn, op2);
- }
-
- pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .adc, 0, rd, rn, op2);
- }
-
- pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .adc, 1, rd, rn, op2);
- }
-
- pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .sbc, 0, rd, rn, op2);
- }
-
- pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .sbc, 1, rd, rn, op2);
- }
-
- pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .rsc, 0, rd, rn, op2);
- }
-
- pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .rsc, 1, rd, rn, op2);
- }
-
- pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .tst, 1, .r0, rn, op2);
- }
-
- pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .teq, 1, .r0, rn, op2);
- }
-
- pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .cmp, 1, .r0, rn, op2);
- }
-
- pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
- }
-
- pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .orr, 0, rd, rn, op2);
- }
-
- pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .orr, 1, rd, rn, op2);
- }
-
- pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .mov, 0, rd, .r0, op2);
- }
-
- pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .mov, 1, rd, .r0, op2);
- }
-
- pub fn bic(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .bic, 0, rd, rn, op2);
- }
-
- pub fn bics(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .bic, 1, rd, rn, op2);
- }
-
- pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .mvn, 0, rd, .r0, op2);
- }
-
- pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .mvn, 1, rd, .r0, op2);
- }
-
- // Integer Saturating Arithmetic
-
- pub fn qadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
- return integerSaturationArithmetic(cond, rd, rm, rn, 0b00);
- }
-
- pub fn qsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
- return integerSaturationArithmetic(cond, rd, rm, rn, 0b01);
- }
-
- pub fn qdadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
- return integerSaturationArithmetic(cond, rd, rm, rn, 0b10);
- }
-
- pub fn qdsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
- return integerSaturationArithmetic(cond, rd, rm, rn, 0b11);
- }
-
- // movw and movt
-
- pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction {
- return specialMov(cond, rd, imm, false);
- }
-
- pub fn movt(cond: Condition, rd: Register, imm: u16) Instruction {
- return specialMov(cond, rd, imm, true);
- }
-
- // PSR transfer
-
- pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction {
- return Instruction{
- .data_processing = .{
- .cond = @enumToInt(cond),
- .i = 0,
- .opcode = if (psr == .spsr) 0b1010 else 0b1000,
- .s = 0,
- .rn = 0b1111,
- .rd = rd.id(),
- .op2 = 0b0000_0000_0000,
- },
- };
- }
-
- pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction {
- return Instruction{
- .data_processing = .{
- .cond = @enumToInt(cond),
- .i = 0,
- .opcode = if (psr == .spsr) 0b1011 else 0b1001,
- .s = 0,
- .rn = 0b1111,
- .rd = 0b1111,
- .op2 = op.toU12(),
- },
- };
- }
-
- // Multiply
-
- pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
- return multiply(cond, 0, rd, rn, rm, null);
- }
-
- pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
- return multiply(cond, 1, rd, rn, rm, null);
- }
-
- pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
- return multiply(cond, 0, rd, rn, rm, ra);
- }
-
- pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
- return multiply(cond, 1, rd, rn, rm, ra);
- }
-
- // Multiply long
-
- pub fn umull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
- return multiplyLong(cond, 0, 0, 0, rdhi, rdlo, rm, rn);
- }
-
- pub fn umulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
- return multiplyLong(cond, 0, 0, 1, rdhi, rdlo, rm, rn);
- }
-
- pub fn umlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
- return multiplyLong(cond, 0, 1, 0, rdhi, rdlo, rm, rn);
- }
-
- pub fn umlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
- return multiplyLong(cond, 0, 1, 1, rdhi, rdlo, rm, rn);
- }
-
- pub fn smull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
- return multiplyLong(cond, 1, 0, 0, rdhi, rdlo, rm, rn);
- }
-
- pub fn smulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
- return multiplyLong(cond, 1, 0, 1, rdhi, rdlo, rm, rn);
- }
-
- pub fn smlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
- return multiplyLong(cond, 1, 1, 0, rdhi, rdlo, rm, rn);
- }
-
- pub fn smlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
- return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn);
- }
-
- // Single data transfer
-
- pub const OffsetArgs = struct {
- pre_index: bool = true,
- positive: bool = true,
- offset: Offset,
- write_back: bool = false,
- };
-
- pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
- return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1);
- }
-
- pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
- return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1);
- }
-
- pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
- return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0);
- }
-
- pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
- return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0);
- }
-
- // Extra load/store
-
- pub const ExtraLoadStoreOffsetArgs = struct {
- pre_index: bool = true,
- positive: bool = true,
- offset: ExtraLoadStoreOffset,
- write_back: bool = false,
- };
-
- pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
- return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0, 0b01, rn, rt, args.offset);
- }
-
- pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
- return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 1, 0b01, rn, rt, args.offset);
- }
-
- // Block data transfer
-
- pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
- return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1);
- }
-
- pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
- return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1);
- }
-
- pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
- return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1);
- }
-
- pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
- return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1);
- }
-
- pub const ldmfa = ldmda;
- pub const ldmea = ldmdb;
- pub const ldmed = ldmib;
- pub const ldmfd = ldmia;
- pub const ldm = ldmia;
-
- pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
- return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0);
- }
-
- pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
- return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0);
- }
-
- pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
- return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0);
- }
-
- pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
- return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0);
- }
-
- pub const stmed = stmda;
- pub const stmfd = stmdb;
- pub const stmfa = stmib;
- pub const stmea = stmia;
- pub const stm = stmia;
-
- // Branch
-
- pub fn b(cond: Condition, offset: i26) Instruction {
- return branch(cond, offset, 0);
- }
-
- pub fn bl(cond: Condition, offset: i26) Instruction {
- return branch(cond, offset, 1);
- }
-
- // Branch and exchange
-
- pub fn bx(cond: Condition, rn: Register) Instruction {
- return branchExchange(cond, rn, 0);
- }
-
- pub fn blx(cond: Condition, rn: Register) Instruction {
- return branchExchange(cond, rn, 1);
- }
-
- // Supervisor Call
-
- pub const swi = svc;
-
- pub fn svc(cond: Condition, comment: u24) Instruction {
- return supervisorCall(cond, comment);
- }
-
- // Breakpoint
-
- pub fn bkpt(imm: u16) Instruction {
- return breakpoint(imm);
- }
-
- // Aliases
-
- pub fn nop() Instruction {
- return mov(.al, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none));
- }
-
- pub fn pop(cond: Condition, args: anytype) Instruction {
- if (@typeInfo(@TypeOf(args)) != .Struct) {
- @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
- }
-
- if (args.len < 1) {
- @compileError("Expected at least one register");
- } else if (args.len == 1) {
- const reg = args[0];
- return ldr(cond, reg, .sp, .{
- .pre_index = false,
- .positive = true,
- .offset = Offset.imm(4),
- .write_back = false,
- });
- } else {
- var register_list: u16 = 0;
- inline for (args) |arg| {
- const reg = @as(Register, arg);
- register_list |= @as(u16, 1) << reg.id();
- }
- return ldm(cond, .sp, true, @bitCast(RegisterList, register_list));
- }
- }
-
- pub fn push(cond: Condition, args: anytype) Instruction {
- if (@typeInfo(@TypeOf(args)) != .Struct) {
- @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
- }
-
- if (args.len < 1) {
- @compileError("Expected at least one register");
- } else if (args.len == 1) {
- const reg = args[0];
- return str(cond, reg, .sp, .{
- .pre_index = true,
- .positive = false,
- .offset = Offset.imm(4),
- .write_back = true,
- });
- } else {
- var register_list: u16 = 0;
- inline for (args) |arg| {
- const reg = @as(Register, arg);
- register_list |= @as(u16, 1) << reg.id();
- }
- return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list));
- }
- }
-
- pub const ShiftAmount = union(enum) {
- immediate: u5,
- register: Register,
-
- pub fn imm(immediate: u5) ShiftAmount {
- return .{
- .immediate = immediate,
- };
- }
-
- pub fn reg(register: Register) ShiftAmount {
- return .{
- .register = register,
- };
- }
- };
-
- pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
- return switch (shift) {
- .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))),
- .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))),
- };
- }
-
- pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
- return switch (shift) {
- .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))),
- .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))),
- };
- }
-
- pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
- return switch (shift) {
- .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))),
- .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))),
- };
- }
-
- pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
- return switch (shift) {
- .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))),
- .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))),
- };
- }
-
- pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
- return switch (shift) {
- .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))),
- .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))),
- };
- }
-
- pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
- return switch (shift) {
- .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))),
- .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))),
- };
- }
-
- pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
- return switch (shift) {
- .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))),
- .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))),
- };
- }
-
- pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
- return switch (shift) {
- .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))),
- .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))),
- };
- }
-};
-
-test "serialize instructions" {
- const Testcase = struct {
- inst: Instruction,
- expected: u32,
- };
-
- const testcases = [_]Testcase{
- .{ // add r0, r0, r0
- .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
- .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
- },
- .{ // mov r4, r2
- .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
- .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
- },
- .{ // mov r0, #42
- .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)),
- .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
- },
- .{ // mrs r5, cpsr
- .inst = Instruction.mrs(.al, .r5, .cpsr),
- .expected = 0b1110_00010_0_001111_0101_000000000000,
- },
- .{ // mul r0, r1, r2
- .inst = Instruction.mul(.al, .r0, .r1, .r2),
- .expected = 0b1110_000000_0_0_0000_0000_0010_1001_0001,
- },
- .{ // umlal r0, r1, r5, r6
- .inst = Instruction.umlal(.al, .r0, .r1, .r5, .r6),
- .expected = 0b1110_00001_0_1_0_0001_0000_0110_1001_0101,
- },
- .{ // ldr r0, [r2, #42]
- .inst = Instruction.ldr(.al, .r0, .r2, .{
- .offset = Instruction.Offset.imm(42),
- }),
- .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
- },
- .{ // str r0, [r3]
- .inst = Instruction.str(.al, .r0, .r3, .{
- .offset = Instruction.Offset.none,
- }),
- .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,
- },
- .{ // strh r1, [r5]
- .inst = Instruction.strh(.al, .r1, .r5, .{
- .offset = Instruction.ExtraLoadStoreOffset.none,
- }),
- .expected = 0b1110_000_1_1_1_0_0_0101_0001_0000_1011_0000,
- },
- .{ // b #12
- .inst = Instruction.b(.al, 12),
- .expected = 0b1110_101_0_0000_0000_0000_0000_0000_0011,
- },
- .{ // bl #-4
- .inst = Instruction.bl(.al, -4),
- .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1111,
- },
- .{ // bx lr
- .inst = Instruction.bx(.al, .lr),
- .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110,
- },
- .{ // svc #0
- .inst = Instruction.svc(.al, 0),
- .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000,
- },
- .{ // bkpt #42
- .inst = Instruction.bkpt(42),
- .expected = 0b1110_0001_0010_000000000010_0111_1010,
- },
- .{ // stmdb r9, {r0}
- .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }),
- .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001,
- },
- .{ // ldmea r4!, {r2, r5}
- .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }),
- .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100,
- },
- .{ // qadd r0, r7, r8
- .inst = Instruction.qadd(.al, .r0, .r7, .r8),
- .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111,
- },
- };
-
- for (testcases) |case| {
- const actual = case.inst.toU32();
- try testing.expectEqual(case.expected, actual);
- }
-}
-
-test "aliases" {
- const Testcase = struct {
- expected: Instruction,
- actual: Instruction,
- };
-
- const testcases = [_]Testcase{
- .{ // pop { r6 }
- .actual = Instruction.pop(.al, .{.r6}),
- .expected = Instruction.ldr(.al, .r6, .sp, .{
- .pre_index = false,
- .positive = true,
- .offset = Instruction.Offset.imm(4),
- .write_back = false,
- }),
- },
- .{ // pop { r1, r5 }
- .actual = Instruction.pop(.al, .{ .r1, .r5 }),
- .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }),
- },
- .{ // push { r3 }
- .actual = Instruction.push(.al, .{.r3}),
- .expected = Instruction.str(.al, .r3, .sp, .{
- .pre_index = true,
- .positive = false,
- .offset = Instruction.Offset.imm(4),
- .write_back = true,
- }),
- },
- .{ // push { r0, r2 }
- .actual = Instruction.push(.al, .{ .r0, .r2 }),
- .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }),
- },
- .{ // lsl r4, r5, #5
- .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)),
- .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg(
- .r5,
- Instruction.Operand.Shift.imm(5, .logical_left),
- )),
- },
- .{ // asrs r1, r1, r3
- .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)),
- .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg(
- .r1,
- Instruction.Operand.Shift.reg(.r3, .arithmetic_right),
- )),
- },
- };
-
- for (testcases) |case| {
- try testing.expectEqual(case.expected.toU32(), case.actual.toU32());
- }
-}