aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/arm.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-02-25 21:04:23 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-02-25 21:04:23 -0700
commit0b58b617998b79a765b54f88fbe90ca2798b3d3e (patch)
treeca6cc4b6bcc2b93166d196049ee49416afe781ad /src/codegen/arm.zig
parentdc325669e360f7a9dfa24f85a62fa386529dade6 (diff)
parentfd208d9d5913a0929e444deb97b91092c427bb14 (diff)
downloadzig-0b58b617998b79a765b54f88fbe90ca2798b3d3e.tar.gz
zig-0b58b617998b79a765b54f88fbe90ca2798b3d3e.zip
Merge remote-tracking branch 'origin/master' into llvm12
Conflicts: * src/clang.zig * src/llvm.zig - this file got moved to src/llvm/bindings.zig in master branch so I had to put the new LLVM arch/os enum tags into it. * lib/std/target.zig, src/stage1/target.cpp - haiku had an inconsistency with its default target ABI, gnu vs eabi. In this commit we make it gnu in both places to match the latest changes by @hoanga. * src/translate_c.zig
Diffstat (limited to 'src/codegen/arm.zig')
-rw-r--r--src/codegen/arm.zig168
1 files changed, 167 insertions, 1 deletions
diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig
index 33ff789648..d538d28c50 100644
--- a/src/codegen/arm.zig
+++ b/src/codegen/arm.zig
@@ -35,8 +35,74 @@ pub const Condition = enum(u4) {
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" {
+ testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorSigned(.eq));
+ testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorUnsigned(.eq));
+
+ testing.expectEqual(@as(Condition, .gt), Condition.fromCompareOperatorSigned(.gt));
+ testing.expectEqual(@as(Condition, .hi), Condition.fromCompareOperatorUnsigned(.gt));
+
+ testing.expectEqual(@as(Condition, .le), Condition.fromCompareOperatorSigned(.lte));
+ testing.expectEqual(@as(Condition, .ls), Condition.fromCompareOperatorUnsigned(.lte));
+}
+
+test "negate condition" {
+ testing.expectEqual(@as(Condition, .eq), Condition.ne.negate());
+ testing.expectEqual(@as(Condition, .ne), Condition.eq.negate());
+}
+
/// Represents a register in the ARM instruction set architecture
pub const Register = enum(u5) {
r0,
@@ -120,7 +186,7 @@ pub const Psr = enum {
spsr,
};
-pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };
+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 };
@@ -174,6 +240,22 @@ pub const Instruction = union(enum) {
fixed: u2 = 0b01,
cond: u4,
},
+ ExtraLoadStore: 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,
+ },
BlockDataTransfer: packed struct {
register_list: u16,
rn: u4,
@@ -402,6 +484,29 @@ pub const Instruction = union(enum) {
}
};
+ /// 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 {
@@ -429,6 +534,7 @@ pub const Instruction = union(enum) {
.Multiply => |v| @bitCast(u32, v),
.MultiplyLong => |v| @bitCast(u32, v),
.SingleDataTransfer => |v| @bitCast(u32, v),
+ .ExtraLoadStore => |v| @bitCast(u32, v),
.BlockDataTransfer => |v| @bitCast(u32, v),
.Branch => |v| @bitCast(u32, v),
.BranchExchange => |v| @bitCast(u32, v),
@@ -551,6 +657,43 @@ pub const Instruction = union(enum) {
};
}
+ 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 => |reg| 0b0000,
+ };
+
+ return Instruction{
+ .ExtraLoadStore = .{
+ .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,
@@ -847,6 +990,23 @@ pub const Instruction = union(enum) {
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 {
@@ -1027,6 +1187,12 @@ test "serialize instructions" {
}),
.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,