| author | |
| committer | |
| log | 8ef80cfaab2a74506d7b3a866dc066d9cd281f55 |
| tree | 5f96c3e02d299e29a9738c74ae429b9f967aac9d |
| parent | db82c1b9820449f2d1e6ef54dd32ec3ffd3c583f |
| signature |
6 files changed, 142 insertions(+), 13 deletions(-)
src/arch/arm/CodeGen.zig+65-4| ... | ... | @@ -439,7 +439,7 @@ fn gen(self: *Self) !void { |
| 439 | 439 | // the code. Therefore, we can just delete |
| 440 | 440 | // the space initially reserved for the |
| 441 | 441 | // jump |
| 442 | self.mir_instructions.len -= 1; | |
| 442 | self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.items[0]); | |
| 443 | 443 | } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| 444 | 444 | self.mir_instructions.set(jmp_reloc, .{ |
| 445 | 445 | .tag = .b, |
| ... | ... | @@ -749,6 +749,17 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u |
| 749 | 749 | /// Use a pointer instruction as the basis for allocating stack memory. |
| 750 | 750 | fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { |
| 751 | 751 | const elem_ty = self.air.typeOfIndex(inst).elemType(); |
| 752 | ||
| 753 | if (!elem_ty.hasRuntimeBits()) { | |
| 754 | // As this stack item will never be dereferenced at runtime, | |
| 755 | // return the current stack offset | |
| 756 | try self.stack.putNoClobber(self.gpa, self.next_stack_offset, .{ | |
| 757 | .inst = inst, | |
| 758 | .size = 0, | |
| 759 | }); | |
| 760 | return self.next_stack_offset; | |
| 761 | } | |
| 762 | ||
| 752 | 763 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { |
| 753 | 764 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty}); |
| 754 | 765 | }; |
| ... | ... | @@ -872,11 +883,61 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 872 | 883 | if (self.liveness.isUnused(inst)) |
| 873 | 884 | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); |
| 874 | 885 | |
| 886 | const operand_ty = self.air.typeOf(ty_op.operand); | |
| 875 | 887 | const operand = try self.resolveInst(ty_op.operand); |
| 876 | _ = operand; | |
| 888 | const info_a = operand_ty.intInfo(self.target.*); | |
| 889 | const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*); | |
| 877 | 890 | |
| 878 | return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch}); | |
| 879 | // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 891 | const result: MCValue = blk: { | |
| 892 | if (info_b.bits <= 32) { | |
| 893 | const operand_reg = switch (operand) { | |
| 894 | .register => |r| r, | |
| 895 | else => operand_reg: { | |
| 896 | if (info_a.bits <= 32) { | |
| 897 | break :operand_reg try self.copyToTmpRegister(operand_ty, operand); | |
| 898 | } else { | |
| 899 | return self.fail("TODO load least significant word into register", .{}); | |
| 900 | } | |
| 901 | }, | |
| 902 | }; | |
| 903 | self.register_manager.freezeRegs(&.{operand_reg}); | |
| 904 | defer self.register_manager.unfreezeRegs(&.{operand_reg}); | |
| 905 | ||
| 906 | const dest_reg = dest_reg: { | |
| 907 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { | |
| 908 | break :dest_reg operand_reg; | |
| 909 | } | |
| 910 | ||
| 911 | break :dest_reg try self.register_manager.allocReg(null); | |
| 912 | }; | |
| 913 | ||
| 914 | switch (info_b.bits) { | |
| 915 | 32 => { | |
| 916 | try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg }); | |
| 917 | break :blk MCValue{ .register = dest_reg }; | |
| 918 | }, | |
| 919 | else => { | |
| 920 | _ = try self.addInst(.{ | |
| 921 | .tag = switch (info_b.signedness) { | |
| 922 | .signed => .sbfx, | |
| 923 | .unsigned => .ubfx, | |
| 924 | }, | |
| 925 | .data = .{ .rr_lsb_width = .{ | |
| 926 | .rd = dest_reg, | |
| 927 | .rn = operand_reg, | |
| 928 | .lsb = 0, | |
| 929 | .width = @intCast(u6, info_b.bits), | |
| 930 | } }, | |
| 931 | }); | |
| 932 | break :blk MCValue{ .register = dest_reg }; | |
| 933 | }, | |
| 934 | } | |
| 935 | } else { | |
| 936 | return self.fail("TODO: truncate to ints > 32 bits", .{}); | |
| 937 | } | |
| 938 | }; | |
| 939 | ||
| 940 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 880 | 941 | } |
| 881 | 942 | |
| 882 | 943 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { |
src/arch/arm/Emit.zig+19| ... | ... | @@ -130,6 +130,9 @@ pub fn emitMir( |
| 130 | 130 | .push => try emit.mirBlockDataTransfer(inst), |
| 131 | 131 | |
| 132 | 132 | .svc => try emit.mirSupervisorCall(inst), |
| 133 | ||
| 134 | .sbfx => try emit.mirBitFieldExtract(inst), | |
| 135 | .ubfx => try emit.mirBitFieldExtract(inst), | |
| 133 | 136 | } |
| 134 | 137 | } |
| 135 | 138 | } |
| ... | ... | @@ -691,3 +694,19 @@ fn mirSupervisorCall(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 691 | 694 | else => unreachable, |
| 692 | 695 | } |
| 693 | 696 | } |
| 697 | ||
| 698 | fn mirBitFieldExtract(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 699 | const tag = emit.mir.instructions.items(.tag)[inst]; | |
| 700 | const cond = emit.mir.instructions.items(.cond)[inst]; | |
| 701 | const rr_lsb_width = emit.mir.instructions.items(.data)[inst].rr_lsb_width; | |
| 702 | const rd = rr_lsb_width.rd; | |
| 703 | const rn = rr_lsb_width.rn; | |
| 704 | const lsb = rr_lsb_width.lsb; | |
| 705 | const width = rr_lsb_width.width; | |
| 706 | ||
| 707 | switch (tag) { | |
| 708 | .sbfx => try emit.writeInstruction(Instruction.sbfx(cond, rd, rn, lsb, width)), | |
| 709 | .ubfx => try emit.writeInstruction(Instruction.ubfx(cond, rd, rn, lsb, width)), | |
| 710 | else => unreachable, | |
| 711 | } | |
| 712 | } |
src/arch/arm/Mir.zig+14| ... | ... | @@ -88,6 +88,8 @@ pub const Inst = struct { |
| 88 | 88 | push, |
| 89 | 89 | /// Reverse Subtract |
| 90 | 90 | rsb, |
| 91 | /// Signed Bit Field Extract | |
| 92 | sbfx, | |
| 91 | 93 | /// Store Register |
| 92 | 94 | str, |
| 93 | 95 | /// Store Register Byte |
| ... | ... | @@ -98,6 +100,8 @@ pub const Inst = struct { |
| 98 | 100 | sub, |
| 99 | 101 | /// Supervisor Call |
| 100 | 102 | svc, |
| 103 | /// Unsigned Bit Field Extract | |
| 104 | ubfx, | |
| 101 | 105 | }; |
| 102 | 106 | |
| 103 | 107 | /// The position of an MIR instruction within the `Mir` instructions array. |
| ... | ... | @@ -179,6 +183,16 @@ pub const Inst = struct { |
| 179 | 183 | rn: Register, |
| 180 | 184 | offset: bits.Instruction.ExtraLoadStoreOffsetArgs, |
| 181 | 185 | }, |
| 186 | /// Two registers and a lsb (range 0-31) and a width (range | |
| 187 | /// 1-32) | |
| 188 | /// | |
| 189 | /// Used by e.g. sbfx | |
| 190 | rr_lsb_width: struct { | |
| 191 | rd: Register, | |
| 192 | rn: Register, | |
| 193 | lsb: u5, | |
| 194 | width: u6, | |
| 195 | }, | |
| 182 | 196 | /// Three registers |
| 183 | 197 | /// |
| 184 | 198 | /// Used by e.g. mul |
src/arch/arm/bits.zig+44| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const DW = std.dwarf; |
| 3 | const assert = std.debug.assert; | |
| 3 | 4 | const testing = std.testing; |
| 4 | 5 | |
| 5 | 6 | /// The condition field specifies the flags necessary for an |
| ... | ... | @@ -237,6 +238,17 @@ pub const Instruction = union(enum) { |
| 237 | 238 | fixed_3: u5 = 0b00010, |
| 238 | 239 | cond: u4, |
| 239 | 240 | }, |
| 241 | bit_field_extract: packed struct { | |
| 242 | rn: u4, | |
| 243 | fixed_1: u3 = 0b101, | |
| 244 | lsb: u5, | |
| 245 | rd: u4, | |
| 246 | widthm1: u5, | |
| 247 | fixed_2: u1 = 0b1, | |
| 248 | unsigned: u1, | |
| 249 | fixed_3: u5 = 0b01111, | |
| 250 | cond: u4, | |
| 251 | }, | |
| 240 | 252 | single_data_transfer: packed struct { |
| 241 | 253 | offset: u12, |
| 242 | 254 | rd: u4, |
| ... | ... | @@ -576,6 +588,7 @@ pub const Instruction = union(enum) { |
| 576 | 588 | .multiply => |v| @bitCast(u32, v), |
| 577 | 589 | .multiply_long => |v| @bitCast(u32, v), |
| 578 | 590 | .integer_saturating_arithmetic => |v| @bitCast(u32, v), |
| 591 | .bit_field_extract => |v| @bitCast(u32, v), | |
| 579 | 592 | .single_data_transfer => |v| @bitCast(u32, v), |
| 580 | 593 | .extra_load_store => |v| @bitCast(u32, v), |
| 581 | 594 | .block_data_transfer => |v| @bitCast(u32, v), |
| ... | ... | @@ -691,6 +704,27 @@ pub const Instruction = union(enum) { |
| 691 | 704 | }; |
| 692 | 705 | } |
| 693 | 706 | |
| 707 | fn bitFieldExtract( | |
| 708 | unsigned: u1, | |
| 709 | cond: Condition, | |
| 710 | rd: Register, | |
| 711 | rn: Register, | |
| 712 | lsb: u5, | |
| 713 | width: u6, | |
| 714 | ) Instruction { | |
| 715 | assert(width > 0 and width <= 32); | |
| 716 | return Instruction{ | |
| 717 | .bit_field_extract = .{ | |
| 718 | .rn = rn.id(), | |
| 719 | .lsb = lsb, | |
| 720 | .rd = rd.id(), | |
| 721 | .widthm1 = @intCast(u5, width - 1), | |
| 722 | .unsigned = unsigned, | |
| 723 | .cond = @enumToInt(cond), | |
| 724 | }, | |
| 725 | }; | |
| 726 | } | |
| 727 | ||
| 694 | 728 | fn singleDataTransfer( |
| 695 | 729 | cond: Condition, |
| 696 | 730 | rd: Register, |
| ... | ... | @@ -1044,6 +1078,16 @@ pub const Instruction = union(enum) { |
| 1044 | 1078 | return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn); |
| 1045 | 1079 | } |
| 1046 | 1080 | |
| 1081 | // Bit field extract | |
| 1082 | ||
| 1083 | pub fn ubfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction { | |
| 1084 | return bitFieldExtract(0b1, cond, rd, rn, lsb, width); | |
| 1085 | } | |
| 1086 | ||
| 1087 | pub fn sbfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction { | |
| 1088 | return bitFieldExtract(0b0, cond, rd, rn, lsb, width); | |
| 1089 | } | |
| 1090 | ||
| 1047 | 1091 | // Single data transfer |
| 1048 | 1092 | |
| 1049 | 1093 | pub const OffsetArgs = struct { |
test/behavior/basic.zig-1| ... | ... | @@ -16,7 +16,6 @@ test "empty function with comments" { |
| 16 | 16 | |
| 17 | 17 | test "truncate" { |
| 18 | 18 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 19 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 20 | 19 | |
| 21 | 20 | try expect(testTruncate(0x10fd) == 0xfd); |
| 22 | 21 | comptime try expect(testTruncate(0x10fd) == 0xfd); |
test/behavior/truncate.zig-8| ... | ... | @@ -4,7 +4,6 @@ const expect = std.testing.expect; |
| 4 | 4 | |
| 5 | 5 | test "truncate u0 to larger integer allowed and has comptime known result" { |
| 6 | 6 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 7 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 8 | 7 | |
| 9 | 8 | var x: u0 = 0; |
| 10 | 9 | const y = @truncate(u8, x); |
| ... | ... | @@ -13,7 +12,6 @@ test "truncate u0 to larger integer allowed and has comptime known result" { |
| 13 | 12 | |
| 14 | 13 | test "truncate.u0.literal" { |
| 15 | 14 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 16 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 17 | 15 | |
| 18 | 16 | var z = @truncate(u0, 0); |
| 19 | 17 | try expect(z == 0); |
| ... | ... | @@ -21,7 +19,6 @@ test "truncate.u0.literal" { |
| 21 | 19 | |
| 22 | 20 | test "truncate.u0.const" { |
| 23 | 21 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 24 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 25 | 22 | |
| 26 | 23 | const c0: usize = 0; |
| 27 | 24 | var z = @truncate(u0, c0); |
| ... | ... | @@ -30,7 +27,6 @@ test "truncate.u0.const" { |
| 30 | 27 | |
| 31 | 28 | test "truncate.u0.var" { |
| 32 | 29 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 33 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 34 | 30 | |
| 35 | 31 | var d: u8 = 2; |
| 36 | 32 | var z = @truncate(u0, d); |
| ... | ... | @@ -39,7 +35,6 @@ test "truncate.u0.var" { |
| 39 | 35 | |
| 40 | 36 | test "truncate i0 to larger integer allowed and has comptime known result" { |
| 41 | 37 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 42 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 43 | 38 | |
| 44 | 39 | var x: i0 = 0; |
| 45 | 40 | const y = @truncate(i8, x); |
| ... | ... | @@ -48,7 +43,6 @@ test "truncate i0 to larger integer allowed and has comptime known result" { |
| 48 | 43 | |
| 49 | 44 | test "truncate.i0.literal" { |
| 50 | 45 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 51 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 52 | 46 | |
| 53 | 47 | var z = @truncate(i0, 0); |
| 54 | 48 | try expect(z == 0); |
| ... | ... | @@ -56,7 +50,6 @@ test "truncate.i0.literal" { |
| 56 | 50 | |
| 57 | 51 | test "truncate.i0.const" { |
| 58 | 52 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 59 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 60 | 53 | |
| 61 | 54 | const c0: isize = 0; |
| 62 | 55 | var z = @truncate(i0, c0); |
| ... | ... | @@ -65,7 +58,6 @@ test "truncate.i0.const" { |
| 65 | 58 | |
| 66 | 59 | test "truncate.i0.var" { |
| 67 | 60 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 68 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 69 | 61 | |
| 70 | 62 | var d: i8 = 2; |
| 71 | 63 | var z = @truncate(i0, d); |