| author | |
| committer | |
| log | 058e482247c73ecb6404c7c1e3d2194f4aa9e059 |
| tree | 3477f4e9793eda2c0e7c0be6cb431db445c473e6 |
| parent | 8349a644d0b9d4b2d58d736a14cdcc567a9e9b89 |
| parent | f48f4baf676061742adb9674bc5e43e82c63f96a |
| signature |
stage2 ARM: implement truncate to integers with <= 32 bits6 files changed, 297 insertions(+), 137 deletions(-)
src/arch/arm/CodeGen.zig+175-109| ... | ... | @@ -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 { |
| ... | ... | @@ -1514,7 +1575,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1514 | 1575 | .compare_flags_signed, .compare_flags_unsigned => unreachable, |
| 1515 | 1576 | .embedded_in_code => unreachable, |
| 1516 | 1577 | .register => |dst_reg| { |
| 1517 | try self.genLdrRegister(dst_reg, reg, elem_size); | |
| 1578 | try self.genLdrRegister(dst_reg, reg, elem_ty); | |
| 1518 | 1579 | }, |
| 1519 | 1580 | .stack_offset => |off| { |
| 1520 | 1581 | if (elem_size <= 4) { |
| ... | ... | @@ -1615,7 +1676,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 1615 | 1676 | |
| 1616 | 1677 | switch (value) { |
| 1617 | 1678 | .register => |value_reg| { |
| 1618 | try self.genStrRegister(value_reg, addr_reg, @intCast(u32, value_ty.abiSize(self.target.*))); | |
| 1679 | try self.genStrRegister(value_reg, addr_reg, value_ty); | |
| 1619 | 1680 | }, |
| 1620 | 1681 | else => { |
| 1621 | 1682 | if (value_ty.abiSize(self.target.*) <= 4) { |
| ... | ... | @@ -2180,68 +2241,71 @@ fn binOp( |
| 2180 | 2241 | } |
| 2181 | 2242 | } |
| 2182 | 2243 | |
| 2183 | fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, abi_size: u32) !void { | |
| 2184 | switch (abi_size) { | |
| 2185 | 1, 3, 4 => { | |
| 2186 | const tag: Mir.Inst.Tag = switch (abi_size) { | |
| 2187 | 1 => .ldrb, | |
| 2188 | 3, 4 => .ldr, | |
| 2189 | else => unreachable, | |
| 2190 | }; | |
| 2244 | fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, ty: Type) !void { | |
| 2245 | const abi_size = ty.abiSize(self.target.*); | |
| 2191 | 2246 | |
| 2192 | _ = try self.addInst(.{ | |
| 2193 | .tag = tag, | |
| 2194 | .data = .{ .rr_offset = .{ | |
| 2195 | .rt = dest_reg, | |
| 2196 | .rn = addr_reg, | |
| 2197 | .offset = .{ .offset = Instruction.Offset.none }, | |
| 2198 | } }, | |
| 2199 | }); | |
| 2200 | }, | |
| 2201 | 2 => { | |
| 2202 | _ = try self.addInst(.{ | |
| 2203 | .tag = .ldrh, | |
| 2204 | .data = .{ .rr_extra_offset = .{ | |
| 2205 | .rt = dest_reg, | |
| 2206 | .rn = addr_reg, | |
| 2207 | .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none }, | |
| 2208 | } }, | |
| 2209 | }); | |
| 2210 | }, | |
| 2211 | else => unreachable, // invalid abi_size for a register | |
| 2212 | } | |
| 2247 | const tag: Mir.Inst.Tag = switch (abi_size) { | |
| 2248 | 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb else .ldrb, | |
| 2249 | 2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh else .ldrh, | |
| 2250 | 3, 4 => .ldr, | |
| 2251 | else => unreachable, | |
| 2252 | }; | |
| 2253 | ||
| 2254 | const rr_offset: Mir.Inst.Data = .{ .rr_offset = .{ | |
| 2255 | .rt = dest_reg, | |
| 2256 | .rn = addr_reg, | |
| 2257 | .offset = .{ .offset = Instruction.Offset.none }, | |
| 2258 | } }; | |
| 2259 | const rr_extra_offset: Mir.Inst.Data = .{ .rr_extra_offset = .{ | |
| 2260 | .rt = dest_reg, | |
| 2261 | .rn = addr_reg, | |
| 2262 | .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none }, | |
| 2263 | } }; | |
| 2264 | ||
| 2265 | const data: Mir.Inst.Data = switch (abi_size) { | |
| 2266 | 1 => if (ty.isSignedInt()) rr_extra_offset else rr_offset, | |
| 2267 | 2 => rr_extra_offset, | |
| 2268 | 3, 4 => rr_offset, | |
| 2269 | else => unreachable, | |
| 2270 | }; | |
| 2271 | ||
| 2272 | _ = try self.addInst(.{ | |
| 2273 | .tag = tag, | |
| 2274 | .data = data, | |
| 2275 | }); | |
| 2213 | 2276 | } |
| 2214 | 2277 | |
| 2215 | fn genStrRegister(self: *Self, source_reg: Register, addr_reg: Register, abi_size: u32) !void { | |
| 2216 | switch (abi_size) { | |
| 2217 | 1, 3, 4 => { | |
| 2218 | const tag: Mir.Inst.Tag = switch (abi_size) { | |
| 2219 | 1 => .strb, | |
| 2220 | 3, 4 => .str, | |
| 2221 | else => unreachable, | |
| 2222 | }; | |
| 2278 | fn genStrRegister(self: *Self, source_reg: Register, addr_reg: Register, ty: Type) !void { | |
| 2279 | const abi_size = ty.abiSize(self.target.*); | |
| 2223 | 2280 | |
| 2224 | _ = try self.addInst(.{ | |
| 2225 | .tag = tag, | |
| 2226 | .data = .{ .rr_offset = .{ | |
| 2227 | .rt = source_reg, | |
| 2228 | .rn = addr_reg, | |
| 2229 | .offset = .{ .offset = Instruction.Offset.none }, | |
| 2230 | } }, | |
| 2231 | }); | |
| 2232 | }, | |
| 2233 | 2 => { | |
| 2234 | _ = try self.addInst(.{ | |
| 2235 | .tag = .strh, | |
| 2236 | .data = .{ .rr_extra_offset = .{ | |
| 2237 | .rt = source_reg, | |
| 2238 | .rn = addr_reg, | |
| 2239 | .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none }, | |
| 2240 | } }, | |
| 2241 | }); | |
| 2242 | }, | |
| 2243 | else => unreachable, // invalid abi_size for a register | |
| 2244 | } | |
| 2281 | const tag: Mir.Inst.Tag = switch (abi_size) { | |
| 2282 | 1 => .strb, | |
| 2283 | 2 => .strh, | |
| 2284 | 3, 4 => .str, | |
| 2285 | else => unreachable, | |
| 2286 | }; | |
| 2287 | ||
| 2288 | const rr_offset: Mir.Inst.Data = .{ .rr_offset = .{ | |
| 2289 | .rt = source_reg, | |
| 2290 | .rn = addr_reg, | |
| 2291 | .offset = .{ .offset = Instruction.Offset.none }, | |
| 2292 | } }; | |
| 2293 | const rr_extra_offset: Mir.Inst.Data = .{ .rr_extra_offset = .{ | |
| 2294 | .rt = source_reg, | |
| 2295 | .rn = addr_reg, | |
| 2296 | .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none }, | |
| 2297 | } }; | |
| 2298 | ||
| 2299 | const data: Mir.Inst.Data = switch (abi_size) { | |
| 2300 | 1, 3, 4 => rr_offset, | |
| 2301 | 2 => rr_extra_offset, | |
| 2302 | else => unreachable, | |
| 2303 | }; | |
| 2304 | ||
| 2305 | _ = try self.addInst(.{ | |
| 2306 | .tag = tag, | |
| 2307 | .data = data, | |
| 2308 | }); | |
| 2245 | 2309 | } |
| 2246 | 2310 | |
| 2247 | 2311 | fn genInlineMemcpy( |
| ... | ... | @@ -2834,8 +2898,6 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2834 | 2898 | } |
| 2835 | 2899 | |
| 2836 | 2900 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2837 | _ = operand; | |
| 2838 | ||
| 2839 | 2901 | const error_type = ty.errorUnionSet(); |
| 2840 | 2902 | const payload_type = ty.errorUnionPayload(); |
| 2841 | 2903 | |
| ... | ... | @@ -3569,55 +3631,59 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3569 | 3631 | // The value is in memory at a hard-coded address. |
| 3570 | 3632 | // If the type is a pointer, it means the pointer address is at this memory location. |
| 3571 | 3633 | try self.genSetReg(ty, reg, .{ .immediate = @intCast(u32, addr) }); |
| 3572 | try self.genLdrRegister(reg, reg, @intCast(u32, ty.abiSize(self.target.*))); | |
| 3634 | try self.genLdrRegister(reg, reg, ty); | |
| 3573 | 3635 | }, |
| 3574 | 3636 | .stack_offset => |unadjusted_off| { |
| 3575 | 3637 | // TODO: maybe addressing from sp instead of fp |
| 3576 | 3638 | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 3577 | 3639 | const adj_off = unadjusted_off + abi_size; |
| 3578 | 3640 | |
| 3579 | switch (abi_size) { | |
| 3580 | 1, 4 => { | |
| 3581 | const offset = if (adj_off <= math.maxInt(u12)) blk: { | |
| 3582 | break :blk Instruction.Offset.imm(@intCast(u12, adj_off)); | |
| 3583 | } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none); | |
| 3641 | const tag: Mir.Inst.Tag = switch (abi_size) { | |
| 3642 | 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb else .ldrb, | |
| 3643 | 2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh else .ldrh, | |
| 3644 | 3, 4 => .ldr, | |
| 3645 | else => unreachable, | |
| 3646 | }; | |
| 3584 | 3647 | |
| 3585 | const tag: Mir.Inst.Tag = switch (abi_size) { | |
| 3586 | 1 => .ldrb, | |
| 3587 | 4 => .ldr, | |
| 3588 | else => unreachable, | |
| 3589 | }; | |
| 3648 | const extra_offset = switch (abi_size) { | |
| 3649 | 1 => ty.isSignedInt(), | |
| 3650 | 2 => true, | |
| 3651 | 3, 4 => false, | |
| 3652 | else => unreachable, | |
| 3653 | }; | |
| 3590 | 3654 | |
| 3591 | _ = try self.addInst(.{ | |
| 3592 | .tag = tag, | |
| 3593 | .data = .{ .rr_offset = .{ | |
| 3594 | .rt = reg, | |
| 3595 | .rn = .fp, | |
| 3596 | .offset = .{ | |
| 3597 | .offset = offset, | |
| 3598 | .positive = false, | |
| 3599 | }, | |
| 3600 | } }, | |
| 3601 | }); | |
| 3602 | }, | |
| 3603 | 2 => { | |
| 3604 | const offset = if (adj_off <= math.maxInt(u8)) blk: { | |
| 3605 | break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); | |
| 3606 | } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off })); | |
| 3655 | if (extra_offset) { | |
| 3656 | const offset = if (adj_off <= math.maxInt(u8)) blk: { | |
| 3657 | break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); | |
| 3658 | } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off })); | |
| 3607 | 3659 | |
| 3608 | _ = try self.addInst(.{ | |
| 3609 | .tag = .ldrh, | |
| 3610 | .data = .{ .rr_extra_offset = .{ | |
| 3611 | .rt = reg, | |
| 3612 | .rn = .fp, | |
| 3613 | .offset = .{ | |
| 3614 | .offset = offset, | |
| 3615 | .positive = false, | |
| 3616 | }, | |
| 3617 | } }, | |
| 3618 | }); | |
| 3619 | }, | |
| 3620 | else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}), | |
| 3660 | _ = try self.addInst(.{ | |
| 3661 | .tag = tag, | |
| 3662 | .data = .{ .rr_extra_offset = .{ | |
| 3663 | .rt = reg, | |
| 3664 | .rn = .fp, | |
| 3665 | .offset = .{ | |
| 3666 | .offset = offset, | |
| 3667 | .positive = false, | |
| 3668 | }, | |
| 3669 | } }, | |
| 3670 | }); | |
| 3671 | } else { | |
| 3672 | const offset = if (adj_off <= math.maxInt(u12)) blk: { | |
| 3673 | break :blk Instruction.Offset.imm(@intCast(u12, adj_off)); | |
| 3674 | } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none); | |
| 3675 | ||
| 3676 | _ = try self.addInst(.{ | |
| 3677 | .tag = tag, | |
| 3678 | .data = .{ .rr_offset = .{ | |
| 3679 | .rt = reg, | |
| 3680 | .rn = .fp, | |
| 3681 | .offset = .{ | |
| 3682 | .offset = offset, | |
| 3683 | .positive = false, | |
| 3684 | }, | |
| 3685 | } }, | |
| 3686 | }); | |
| 3621 | 3687 | } |
| 3622 | 3688 | }, |
| 3623 | 3689 | .stack_argument_offset => |unadjusted_off| { |
| ... | ... | @@ -3625,9 +3691,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3625 | 3691 | const adj_off = unadjusted_off + abi_size; |
| 3626 | 3692 | |
| 3627 | 3693 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 3628 | 1 => .ldrb_stack_argument, | |
| 3629 | 2 => .ldrh_stack_argument, | |
| 3630 | 4 => .ldr_stack_argument, | |
| 3694 | 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_stack_argument else .ldrb_stack_argument, | |
| 3695 | 2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_stack_argument else .ldrh_stack_argument, | |
| 3696 | 3, 4 => .ldr_stack_argument, | |
| 3631 | 3697 | else => unreachable, |
| 3632 | 3698 | }; |
| 3633 | 3699 |
src/arch/arm/Emit.zig+46-15| ... | ... | @@ -115,8 +115,12 @@ pub fn emitMir( |
| 115 | 115 | .ldr_stack_argument => try emit.mirLoadStackArgument(inst), |
| 116 | 116 | .ldrb_stack_argument => try emit.mirLoadStackArgument(inst), |
| 117 | 117 | .ldrh_stack_argument => try emit.mirLoadStackArgument(inst), |
| 118 | .ldrsb_stack_argument => try emit.mirLoadStackArgument(inst), | |
| 119 | .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst), | |
| 118 | 120 | |
| 119 | 121 | .ldrh => try emit.mirLoadStoreExtra(inst), |
| 122 | .ldrsb => try emit.mirLoadStore(inst), | |
| 123 | .ldrsh => try emit.mirLoadStoreExtra(inst), | |
| 120 | 124 | .strh => try emit.mirLoadStoreExtra(inst), |
| 121 | 125 | |
| 122 | 126 | .movw => try emit.mirSpecialMove(inst), |
| ... | ... | @@ -130,6 +134,9 @@ pub fn emitMir( |
| 130 | 134 | .push => try emit.mirBlockDataTransfer(inst), |
| 131 | 135 | |
| 132 | 136 | .svc => try emit.mirSupervisorCall(inst), |
| 137 | ||
| 138 | .sbfx => try emit.mirBitFieldExtract(inst), | |
| 139 | .ubfx => try emit.mirBitFieldExtract(inst), | |
| 133 | 140 | } |
| 134 | 141 | } |
| 135 | 142 | } |
| ... | ... | @@ -590,36 +597,42 @@ fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 590 | 597 | |
| 591 | 598 | const raw_offset = emit.prologue_stack_space - r_stack_offset.stack_offset; |
| 592 | 599 | switch (tag) { |
| 593 | .ldr_stack_argument => { | |
| 600 | .ldr_stack_argument, | |
| 601 | .ldrb_stack_argument, | |
| 602 | => { | |
| 594 | 603 | const offset = if (raw_offset <= math.maxInt(u12)) blk: { |
| 595 | 604 | break :blk Instruction.Offset.imm(@intCast(u12, raw_offset)); |
| 596 | 605 | } else return emit.fail("TODO mirLoadStack larger offsets", .{}); |
| 597 | 606 | |
| 598 | try emit.writeInstruction(Instruction.ldr( | |
| 599 | cond, | |
| 600 | r_stack_offset.rt, | |
| 601 | .fp, | |
| 602 | .{ .offset = offset }, | |
| 603 | )); | |
| 604 | }, | |
| 605 | .ldrb_stack_argument => { | |
| 606 | const offset = if (raw_offset <= math.maxInt(u12)) blk: { | |
| 607 | break :blk Instruction.Offset.imm(@intCast(u12, raw_offset)); | |
| 608 | } else return emit.fail("TODO mirLoadStack larger offsets", .{}); | |
| 607 | const ldr = switch (tag) { | |
| 608 | .ldr_stack_argument => Instruction.ldr, | |
| 609 | .ldrb_stack_argument => Instruction.ldrb, | |
| 610 | else => unreachable, | |
| 611 | }; | |
| 609 | 612 | |
| 610 | try emit.writeInstruction(Instruction.ldrb( | |
| 613 | try emit.writeInstruction(ldr( | |
| 611 | 614 | cond, |
| 612 | 615 | r_stack_offset.rt, |
| 613 | 616 | .fp, |
| 614 | 617 | .{ .offset = offset }, |
| 615 | 618 | )); |
| 616 | 619 | }, |
| 617 | .ldrh_stack_argument => { | |
| 620 | .ldrh_stack_argument, | |
| 621 | .ldrsb_stack_argument, | |
| 622 | .ldrsh_stack_argument, | |
| 623 | => { | |
| 618 | 624 | const offset = if (raw_offset <= math.maxInt(u8)) blk: { |
| 619 | 625 | break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, raw_offset)); |
| 620 | 626 | } else return emit.fail("TODO mirLoadStack larger offsets", .{}); |
| 621 | 627 | |
| 622 | try emit.writeInstruction(Instruction.ldrh( | |
| 628 | const ldr = switch (tag) { | |
| 629 | .ldrh_stack_argument => Instruction.ldrh, | |
| 630 | .ldrsb_stack_argument => Instruction.ldrsb, | |
| 631 | .ldrsh_stack_argument => Instruction.ldrsh, | |
| 632 | else => unreachable, | |
| 633 | }; | |
| 634 | ||
| 635 | try emit.writeInstruction(ldr( | |
| 623 | 636 | cond, |
| 624 | 637 | r_stack_offset.rt, |
| 625 | 638 | .fp, |
| ... | ... | @@ -637,6 +650,8 @@ fn mirLoadStoreExtra(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 637 | 650 | |
| 638 | 651 | switch (tag) { |
| 639 | 652 | .ldrh => try emit.writeInstruction(Instruction.ldrh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)), |
| 653 | .ldrsb => try emit.writeInstruction(Instruction.ldrsb(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)), | |
| 654 | .ldrsh => try emit.writeInstruction(Instruction.ldrsh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)), | |
| 640 | 655 | .strh => try emit.writeInstruction(Instruction.strh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)), |
| 641 | 656 | else => unreachable, |
| 642 | 657 | } |
| ... | ... | @@ -691,3 +706,19 @@ fn mirSupervisorCall(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 691 | 706 | else => unreachable, |
| 692 | 707 | } |
| 693 | 708 | } |
| 709 | ||
| 710 | fn mirBitFieldExtract(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 711 | const tag = emit.mir.instructions.items(.tag)[inst]; | |
| 712 | const cond = emit.mir.instructions.items(.cond)[inst]; | |
| 713 | const rr_lsb_width = emit.mir.instructions.items(.data)[inst].rr_lsb_width; | |
| 714 | const rd = rr_lsb_width.rd; | |
| 715 | const rn = rr_lsb_width.rn; | |
| 716 | const lsb = rr_lsb_width.lsb; | |
| 717 | const width = rr_lsb_width.width; | |
| 718 | ||
| 719 | switch (tag) { | |
| 720 | .sbfx => try emit.writeInstruction(Instruction.sbfx(cond, rd, rn, lsb, width)), | |
| 721 | .ubfx => try emit.writeInstruction(Instruction.ubfx(cond, rd, rn, lsb, width)), | |
| 722 | else => unreachable, | |
| 723 | } | |
| 724 | } |
src/arch/arm/Mir.zig+22| ... | ... | @@ -64,6 +64,14 @@ pub const Inst = struct { |
| 64 | 64 | ldrh, |
| 65 | 65 | /// Load Register Halfword |
| 66 | 66 | ldrh_stack_argument, |
| 67 | /// Load Register Signed Byte | |
| 68 | ldrsb, | |
| 69 | /// Load Register Signed Byte | |
| 70 | ldrsb_stack_argument, | |
| 71 | /// Load Register Signed Halfword | |
| 72 | ldrsh, | |
| 73 | /// Load Register Signed Halfword | |
| 74 | ldrsh_stack_argument, | |
| 67 | 75 | /// Logical Shift Left |
| 68 | 76 | lsl, |
| 69 | 77 | /// Logical Shift Right |
| ... | ... | @@ -88,6 +96,8 @@ pub const Inst = struct { |
| 88 | 96 | push, |
| 89 | 97 | /// Reverse Subtract |
| 90 | 98 | rsb, |
| 99 | /// Signed Bit Field Extract | |
| 100 | sbfx, | |
| 91 | 101 | /// Store Register |
| 92 | 102 | str, |
| 93 | 103 | /// Store Register Byte |
| ... | ... | @@ -98,6 +108,8 @@ pub const Inst = struct { |
| 98 | 108 | sub, |
| 99 | 109 | /// Supervisor Call |
| 100 | 110 | svc, |
| 111 | /// Unsigned Bit Field Extract | |
| 112 | ubfx, | |
| 101 | 113 | }; |
| 102 | 114 | |
| 103 | 115 | /// The position of an MIR instruction within the `Mir` instructions array. |
| ... | ... | @@ -179,6 +191,16 @@ pub const Inst = struct { |
| 179 | 191 | rn: Register, |
| 180 | 192 | offset: bits.Instruction.ExtraLoadStoreOffsetArgs, |
| 181 | 193 | }, |
| 194 | /// Two registers and a lsb (range 0-31) and a width (range | |
| 195 | /// 1-32) | |
| 196 | /// | |
| 197 | /// Used by e.g. sbfx | |
| 198 | rr_lsb_width: struct { | |
| 199 | rd: Register, | |
| 200 | rn: Register, | |
| 201 | lsb: u5, | |
| 202 | width: u6, | |
| 203 | }, | |
| 182 | 204 | /// Three registers |
| 183 | 205 | /// |
| 184 | 206 | /// Used by e.g. mul |
src/arch/arm/bits.zig+54-2| ... | ... | @@ -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 { |
| ... | ... | @@ -1079,11 +1123,19 @@ pub const Instruction = union(enum) { |
| 1079 | 1123 | }; |
| 1080 | 1124 | |
| 1081 | 1125 | pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { |
| 1082 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0, 0b01, rn, rt, args.offset); | |
| 1126 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b0, 0b01, rn, rt, args.offset); | |
| 1083 | 1127 | } |
| 1084 | 1128 | |
| 1085 | 1129 | pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { |
| 1086 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 1, 0b01, rn, rt, args.offset); | |
| 1130 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b01, rn, rt, args.offset); | |
| 1131 | } | |
| 1132 | ||
| 1133 | pub fn ldrsh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | |
| 1134 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b11, rn, rt, args.offset); | |
| 1135 | } | |
| 1136 | ||
| 1137 | pub fn ldrsb(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | |
| 1138 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b10, rn, rt, args.offset); | |
| 1087 | 1139 | } |
| 1088 | 1140 | |
| 1089 | 1141 | // Block data transfer |
test/behavior/basic.zig-2| ... | ... | @@ -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); |
| ... | ... | @@ -27,7 +26,6 @@ fn testTruncate(x: u32) u8 { |
| 27 | 26 | |
| 28 | 27 | test "truncate to non-power-of-two integers" { |
| 29 | 28 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 30 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 31 | 29 | |
| 32 | 30 | try testTrunc(u32, u1, 0b10101, 0b1); |
| 33 | 31 | try testTrunc(u32, u1, 0b10110, 0b0); |
test/behavior/truncate.zig-9| ... | ... | @@ -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); |
| ... | ... | @@ -74,7 +66,6 @@ test "truncate.i0.var" { |
| 74 | 66 | |
| 75 | 67 | test "truncate on comptime integer" { |
| 76 | 68 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 77 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 78 | 69 | |
| 79 | 70 | var x = @truncate(u16, 9999); |
| 80 | 71 | try expect(x == 9999); |