diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-02-25 21:04:23 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-02-25 21:04:23 -0700 |
| commit | 0b58b617998b79a765b54f88fbe90ca2798b3d3e (patch) | |
| tree | ca6cc4b6bcc2b93166d196049ee49416afe781ad /src/codegen/arm.zig | |
| parent | dc325669e360f7a9dfa24f85a62fa386529dade6 (diff) | |
| parent | fd208d9d5913a0929e444deb97b91092c427bb14 (diff) | |
| download | zig-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.zig | 168 |
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, |
