| author | |
| committer | |
| log | f95a8ddafa53d4cee591c82bd5ed3eb0eac4a51b |
| tree | 36b7429dcfedc02cbed4c310e5f72f868b913882 |
| parent | c78daeb642e742af3ac42bac0468776ccc4cd452 |
| signature |
5 files changed, 224 insertions(+), 53 deletions(-)
src/arch/aarch64/CodeGen.zig+105-17| ... | @@ -939,14 +939,99 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -939,14 +939,99 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 939 | return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}); | 939 | return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}); |
| 940 | } | 940 | } |
| 941 | 941 | ||
| 942 | fn truncRegister( | ||
| 943 | self: *Self, | ||
| 944 | operand_reg: Register, | ||
| 945 | dest_reg: Register, | ||
| 946 | int_signedness: std.builtin.Signedness, | ||
| 947 | int_bits: u16, | ||
| 948 | ) !void { | ||
| 949 | switch (int_bits) { | ||
| 950 | 1...31, 33...63 => { | ||
| 951 | _ = try self.addInst(.{ | ||
| 952 | .tag = switch (int_signedness) { | ||
| 953 | .signed => .sbfx, | ||
| 954 | .unsigned => .ubfx, | ||
| 955 | }, | ||
| 956 | .data = .{ .rr_lsb_width = .{ | ||
| 957 | .rd = dest_reg, | ||
| 958 | .rn = operand_reg, | ||
| 959 | .lsb = 0, | ||
| 960 | .width = @intCast(u6, int_bits), | ||
| 961 | } }, | ||
| 962 | }); | ||
| 963 | }, | ||
| 964 | 32, 64 => { | ||
| 965 | _ = try self.addInst(.{ | ||
| 966 | .tag = .mov_register, | ||
| 967 | .data = .{ .rr = .{ | ||
| 968 | .rd = dest_reg, | ||
| 969 | .rn = operand_reg, | ||
| 970 | } }, | ||
| 971 | }); | ||
| 972 | }, | ||
| 973 | else => unreachable, | ||
| 974 | } | ||
| 975 | } | ||
| 976 | |||
| 977 | fn trunc( | ||
| 978 | self: *Self, | ||
| 979 | maybe_inst: ?Air.Inst.Index, | ||
| 980 | operand: MCValue, | ||
| 981 | operand_ty: Type, | ||
| 982 | dest_ty: Type, | ||
| 983 | ) !MCValue { | ||
| 984 | const info_a = operand_ty.intInfo(self.target.*); | ||
| 985 | const info_b = dest_ty.intInfo(self.target.*); | ||
| 986 | |||
| 987 | if (info_b.bits <= 64) { | ||
| 988 | const operand_reg = switch (operand) { | ||
| 989 | .register => |r| r, | ||
| 990 | else => operand_reg: { | ||
| 991 | if (info_a.bits <= 64) { | ||
| 992 | const raw_reg = try self.copyToTmpRegister(operand_ty, operand); | ||
| 993 | break :operand_reg registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); | ||
| 994 | } else { | ||
| 995 | return self.fail("TODO load least significant word into register", .{}); | ||
| 996 | } | ||
| 997 | }, | ||
| 998 | }; | ||
| 999 | self.register_manager.freezeRegs(&.{operand_reg}); | ||
| 1000 | defer self.register_manager.unfreezeRegs(&.{operand_reg}); | ||
| 1001 | |||
| 1002 | const dest_reg = if (maybe_inst) |inst| blk: { | ||
| 1003 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | ||
| 1004 | |||
| 1005 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { | ||
| 1006 | break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*)); | ||
| 1007 | } else { | ||
| 1008 | const raw_reg = try self.register_manager.allocReg(inst); | ||
| 1009 | break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); | ||
| 1010 | } | ||
| 1011 | } else blk: { | ||
| 1012 | const raw_reg = try self.register_manager.allocReg(null); | ||
| 1013 | break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); | ||
| 1014 | }; | ||
| 1015 | |||
| 1016 | try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); | ||
| 1017 | |||
| 1018 | return MCValue{ .register = dest_reg }; | ||
| 1019 | } else { | ||
| 1020 | return self.fail("TODO: truncate to ints > 32 bits", .{}); | ||
| 1021 | } | ||
| 1022 | } | ||
| 1023 | |||
| 942 | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { | 1024 | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 943 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1025 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 944 | if (self.liveness.isUnused(inst)) | ||
| 945 | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); | ||
| 946 | |||
| 947 | const operand = try self.resolveInst(ty_op.operand); | 1026 | const operand = try self.resolveInst(ty_op.operand); |
| 948 | _ = operand; | 1027 | const operand_ty = self.air.typeOf(ty_op.operand); |
| 949 | return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch}); | 1028 | const dest_ty = self.air.typeOfIndex(inst); |
| 1029 | |||
| 1030 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { | ||
| 1031 | break :blk try self.trunc(inst, operand, operand_ty, dest_ty); | ||
| 1032 | }; | ||
| 1033 | |||
| 1034 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | ||
| 950 | } | 1035 | } |
| 951 | 1036 | ||
| 952 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { | 1037 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -3483,23 +3568,26 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -3483,23 +3568,26 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3483 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } }, | 3568 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } }, |
| 3484 | }); | 3569 | }); |
| 3485 | 3570 | ||
| 3486 | if (x > math.maxInt(u16)) { | 3571 | if (x & 0x0000_0000_ffff_0000 != 0) { |
| 3487 | _ = try self.addInst(.{ | 3572 | _ = try self.addInst(.{ |
| 3488 | .tag = .movk, | 3573 | .tag = .movk, |
| 3489 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } }, | 3574 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } }, |
| 3490 | }); | 3575 | }); |
| 3491 | } | 3576 | } |
| 3492 | if (x > math.maxInt(u32)) { | 3577 | |
| 3493 | _ = try self.addInst(.{ | 3578 | if (reg.size() == 64) { |
| 3494 | .tag = .movk, | 3579 | if (x & 0x0000_ffff_0000_0000 != 0) { |
| 3495 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } }, | 3580 | _ = try self.addInst(.{ |
| 3496 | }); | 3581 | .tag = .movk, |
| 3497 | } | 3582 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } }, |
| 3498 | if (x > math.maxInt(u48)) { | 3583 | }); |
| 3499 | _ = try self.addInst(.{ | 3584 | } |
| 3500 | .tag = .movk, | 3585 | if (x & 0xffff_0000_0000_0000 != 0) { |
| 3501 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } }, | 3586 | _ = try self.addInst(.{ |
| 3502 | }); | 3587 | .tag = .movk, |
| 3588 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } }, | ||
| 3589 | }); | ||
| 3590 | } | ||
| 3503 | } | 3591 | } |
| 3504 | }, | 3592 | }, |
| 3505 | .register => |src_reg| { | 3593 | .register => |src_reg| { |
src/arch/aarch64/Emit.zig+40| ... | @@ -162,6 +162,17 @@ pub fn emitMir( | ... | @@ -162,6 +162,17 @@ pub fn emitMir( |
| 162 | 162 | ||
| 163 | .push_regs => try emit.mirPushPopRegs(inst), | 163 | .push_regs => try emit.mirPushPopRegs(inst), |
| 164 | .pop_regs => try emit.mirPushPopRegs(inst), | 164 | .pop_regs => try emit.mirPushPopRegs(inst), |
| 165 | |||
| 166 | .sbfx, | ||
| 167 | .ubfx, | ||
| 168 | => try emit.mirBitfieldExtract(inst), | ||
| 169 | |||
| 170 | .sxtb, | ||
| 171 | .sxth, | ||
| 172 | .sxtw, | ||
| 173 | .uxtb, | ||
| 174 | .uxth, | ||
| 175 | => try emit.mirExtend(inst), | ||
| 165 | } | 176 | } |
| 166 | } | 177 | } |
| 167 | } | 178 | } |
| ... | @@ -1050,3 +1061,32 @@ fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -1050,3 +1061,32 @@ fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 1050 | else => unreachable, | 1061 | else => unreachable, |
| 1051 | } | 1062 | } |
| 1052 | } | 1063 | } |
| 1064 | |||
| 1065 | fn mirBitfieldExtract(emit: *Emit, inst: Mir.Inst.Index) !void { | ||
| 1066 | const tag = emit.mir.instructions.items(.tag)[inst]; | ||
| 1067 | const rr_lsb_width = emit.mir.instructions.items(.data)[inst].rr_lsb_width; | ||
| 1068 | const rd = rr_lsb_width.rd; | ||
| 1069 | const rn = rr_lsb_width.rn; | ||
| 1070 | const lsb = rr_lsb_width.lsb; | ||
| 1071 | const width = rr_lsb_width.width; | ||
| 1072 | |||
| 1073 | switch (tag) { | ||
| 1074 | .sbfx => try emit.writeInstruction(Instruction.sbfx(rd, rn, lsb, width)), | ||
| 1075 | .ubfx => try emit.writeInstruction(Instruction.ubfx(rd, rn, lsb, width)), | ||
| 1076 | else => unreachable, | ||
| 1077 | } | ||
| 1078 | } | ||
| 1079 | |||
| 1080 | fn mirExtend(emit: *Emit, inst: Mir.Inst.Index) !void { | ||
| 1081 | const tag = emit.mir.instructions.items(.tag)[inst]; | ||
| 1082 | const rr = emit.mir.instructions.items(.data)[inst].rr; | ||
| 1083 | |||
| 1084 | switch (tag) { | ||
| 1085 | .sxtb => try emit.writeInstruction(Instruction.sxtb(rr.rd, rr.rn)), | ||
| 1086 | .sxth => try emit.writeInstruction(Instruction.sxth(rr.rd, rr.rn)), | ||
| 1087 | .sxtw => try emit.writeInstruction(Instruction.sxtw(rr.rd, rr.rn)), | ||
| 1088 | .uxtb => try emit.writeInstruction(Instruction.uxtb(rr.rd, rr.rn)), | ||
| 1089 | .uxth => try emit.writeInstruction(Instruction.uxth(rr.rd, rr.rn)), | ||
| 1090 | else => unreachable, | ||
| 1091 | } | ||
| 1092 | } |
src/arch/aarch64/Mir.zig+31-7| ... | @@ -130,6 +130,14 @@ pub const Inst = struct { | ... | @@ -130,6 +130,14 @@ pub const Inst = struct { |
| 130 | push_regs, | 130 | push_regs, |
| 131 | /// Return from subroutine | 131 | /// Return from subroutine |
| 132 | ret, | 132 | ret, |
| 133 | /// Signed bitfield extract | ||
| 134 | sbfx, | ||
| 135 | /// Signed extend byte | ||
| 136 | sxtb, | ||
| 137 | /// Signed extend halfword | ||
| 138 | sxth, | ||
| 139 | /// Signed extend word | ||
| 140 | sxtw, | ||
| 133 | /// Store Pair of Registers | 141 | /// Store Pair of Registers |
| 134 | stp, | 142 | stp, |
| 135 | /// Pseudo-instruction: Store to stack | 143 | /// Pseudo-instruction: Store to stack |
| ... | @@ -156,6 +164,12 @@ pub const Inst = struct { | ... | @@ -156,6 +164,12 @@ pub const Inst = struct { |
| 156 | sub_shifted_register, | 164 | sub_shifted_register, |
| 157 | /// Supervisor Call | 165 | /// Supervisor Call |
| 158 | svc, | 166 | svc, |
| 167 | /// Unsigned bitfield extract | ||
| 168 | ubfx, | ||
| 169 | /// Unsigned extend byte | ||
| 170 | uxtb, | ||
| 171 | /// Unsigned extend halfword | ||
| 172 | uxth, | ||
| 159 | }; | 173 | }; |
| 160 | 174 | ||
| 161 | /// The position of an MIR instruction within the `Mir` instructions array. | 175 | /// The position of an MIR instruction within the `Mir` instructions array. |
| ... | @@ -225,13 +239,6 @@ pub const Inst = struct { | ... | @@ -225,13 +239,6 @@ pub const Inst = struct { |
| 225 | rt: Register, | 239 | rt: Register, |
| 226 | inst: Index, | 240 | inst: Index, |
| 227 | }, | 241 | }, |
| 228 | /// Two registers | ||
| 229 | /// | ||
| 230 | /// Used by e.g. mov_register | ||
| 231 | rr: struct { | ||
| 232 | rd: Register, | ||
| 233 | rn: Register, | ||
| 234 | }, | ||
| 235 | /// A register, an unsigned 12-bit immediate, and an optional shift | 242 | /// A register, an unsigned 12-bit immediate, and an optional shift |
| 236 | /// | 243 | /// |
| 237 | /// Used by e.g. cmp_immediate | 244 | /// Used by e.g. cmp_immediate |
| ... | @@ -240,6 +247,13 @@ pub const Inst = struct { | ... | @@ -240,6 +247,13 @@ pub const Inst = struct { |
| 240 | imm12: u12, | 247 | imm12: u12, |
| 241 | sh: u1 = 0, | 248 | sh: u1 = 0, |
| 242 | }, | 249 | }, |
| 250 | /// Two registers | ||
| 251 | /// | ||
| 252 | /// Used by e.g. mov_register | ||
| 253 | rr: struct { | ||
| 254 | rd: Register, | ||
| 255 | rn: Register, | ||
| 256 | }, | ||
| 243 | /// Two registers, an unsigned 12-bit immediate, and an optional shift | 257 | /// Two registers, an unsigned 12-bit immediate, and an optional shift |
| 244 | /// | 258 | /// |
| 245 | /// Used by e.g. sub_immediate | 259 | /// Used by e.g. sub_immediate |
| ... | @@ -268,6 +282,16 @@ pub const Inst = struct { | ... | @@ -268,6 +282,16 @@ pub const Inst = struct { |
| 268 | imm6: u6, | 282 | imm6: u6, |
| 269 | shift: bits.Instruction.LogicalShiftedRegisterShift, | 283 | shift: bits.Instruction.LogicalShiftedRegisterShift, |
| 270 | }, | 284 | }, |
| 285 | /// Two registers and a lsb (range 0-63) and a width (range | ||
| 286 | /// 1-64) | ||
| 287 | /// | ||
| 288 | /// Used by e.g. ubfx | ||
| 289 | rr_lsb_width: struct { | ||
| 290 | rd: Register, | ||
| 291 | rn: Register, | ||
| 292 | lsb: u6, | ||
| 293 | width: u7, | ||
| 294 | }, | ||
| 271 | /// Two registers and a bitmask immediate | 295 | /// Two registers and a bitmask immediate |
| 272 | /// | 296 | /// |
| 273 | /// Used by e.g. eor_immediate | 297 | /// Used by e.g. eor_immediate |
src/arch/aarch64/bits.zig+48-27| ... | @@ -510,33 +510,23 @@ pub const Instruction = union(enum) { | ... | @@ -510,33 +510,23 @@ pub const Instruction = union(enum) { |
| 510 | imm16: u16, | 510 | imm16: u16, |
| 511 | shift: u6, | 511 | shift: u6, |
| 512 | ) Instruction { | 512 | ) Instruction { |
| 513 | switch (rd.size()) { | 513 | assert(shift % 16 == 0); |
| 514 | 32 => { | 514 | assert(!(rd.size() == 32 and shift > 16)); |
| 515 | assert(shift % 16 == 0 and shift <= 16); | 515 | assert(!(rd.size() == 64 and shift > 48)); |
| 516 | return Instruction{ | 516 | |
| 517 | .move_wide_immediate = .{ | 517 | return Instruction{ |
| 518 | .rd = rd.enc(), | 518 | .move_wide_immediate = .{ |
| 519 | .imm16 = imm16, | 519 | .rd = rd.enc(), |
| 520 | .hw = @intCast(u2, shift / 16), | 520 | .imm16 = imm16, |
| 521 | .opc = opc, | 521 | .hw = @intCast(u2, shift / 16), |
| 522 | .sf = 0, | 522 | .opc = opc, |
| 523 | }, | 523 | .sf = switch (rd.size()) { |
| 524 | }; | 524 | 32 => 0, |
| 525 | }, | 525 | 64 => 1, |
| 526 | 64 => { | 526 | else => unreachable, // unexpected register size |
| 527 | assert(shift % 16 == 0 and shift <= 48); | 527 | }, |
| 528 | return Instruction{ | ||
| 529 | .move_wide_immediate = .{ | ||
| 530 | .rd = rd.enc(), | ||
| 531 | .imm16 = imm16, | ||
| 532 | .hw = @intCast(u2, shift / 16), | ||
| 533 | .opc = opc, | ||
| 534 | .sf = 1, | ||
| 535 | }, | ||
| 536 | }; | ||
| 537 | }, | 528 | }, |
| 538 | else => unreachable, // unexpected register size | 529 | }; |
| 539 | } | ||
| 540 | } | 530 | } |
| 541 | 531 | ||
| 542 | fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction { | 532 | fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction { |
| ... | @@ -914,7 +904,7 @@ pub const Instruction = union(enum) { | ... | @@ -914,7 +904,7 @@ pub const Instruction = union(enum) { |
| 914 | n: u1, | 904 | n: u1, |
| 915 | ) Instruction { | 905 | ) Instruction { |
| 916 | assert(rd.size() == rn.size()); | 906 | assert(rd.size() == rn.size()); |
| 917 | assert(!(rd.size() == 32 and n == 1)); | 907 | assert(!(rd.size() == 32 and n != 0)); |
| 918 | 908 | ||
| 919 | return Instruction{ | 909 | return Instruction{ |
| 920 | .logical_immediate = .{ | 910 | .logical_immediate = .{ |
| ... | @@ -942,6 +932,8 @@ pub const Instruction = union(enum) { | ... | @@ -942,6 +932,8 @@ pub const Instruction = union(enum) { |
| 942 | imms: u6, | 932 | imms: u6, |
| 943 | ) Instruction { | 933 | ) Instruction { |
| 944 | assert(rd.size() == rn.size()); | 934 | assert(rd.size() == rn.size()); |
| 935 | assert(!(rd.size() == 64 and n != 1)); | ||
| 936 | assert(!(rd.size() == 32 and (n != 0 or immr >> 5 != 0 or immr >> 5 != 0))); | ||
| 945 | 937 | ||
| 946 | return Instruction{ | 938 | return Instruction{ |
| 947 | .bitfield = .{ | 939 | .bitfield = .{ |
| ... | @@ -1417,6 +1409,23 @@ pub const Instruction = union(enum) { | ... | @@ -1417,6 +1409,23 @@ pub const Instruction = union(enum) { |
| 1417 | return sbfm(rd, rn, shift, imms); | 1409 | return sbfm(rd, rn, shift, imms); |
| 1418 | } | 1410 | } |
| 1419 | 1411 | ||
| 1412 | pub fn sbfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction { | ||
| 1413 | return sbfm(rd, rn, lsb, @intCast(u6, lsb + width - 1)); | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | pub fn sxtb(rd: Register, rn: Register) Instruction { | ||
| 1417 | return sbfm(rd, rn, 0, 7); | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | pub fn sxth(rd: Register, rn: Register) Instruction { | ||
| 1421 | return sbfm(rd, rn, 0, 15); | ||
| 1422 | } | ||
| 1423 | |||
| 1424 | pub fn sxtw(rd: Register, rn: Register) Instruction { | ||
| 1425 | assert(rd.size() == 64); | ||
| 1426 | return sbfm(rd, rn, 0, 31); | ||
| 1427 | } | ||
| 1428 | |||
| 1420 | pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction { | 1429 | pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction { |
| 1421 | const size = @intCast(u6, rd.size() - 1); | 1430 | const size = @intCast(u6, rd.size() - 1); |
| 1422 | return ubfm(rd, rn, size - shift + 1, size - shift); | 1431 | return ubfm(rd, rn, size - shift + 1, size - shift); |
| ... | @@ -1427,6 +1436,18 @@ pub const Instruction = union(enum) { | ... | @@ -1427,6 +1436,18 @@ pub const Instruction = union(enum) { |
| 1427 | return ubfm(rd, rn, shift, imms); | 1436 | return ubfm(rd, rn, shift, imms); |
| 1428 | } | 1437 | } |
| 1429 | 1438 | ||
| 1439 | pub fn ubfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction { | ||
| 1440 | return ubfm(rd, rn, lsb, @intCast(u6, lsb + width - 1)); | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | pub fn uxtb(rd: Register, rn: Register) Instruction { | ||
| 1444 | return ubfm(rd, rn, 0, 7); | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | pub fn uxth(rd: Register, rn: Register) Instruction { | ||
| 1448 | return ubfm(rd, rn, 0, 15); | ||
| 1449 | } | ||
| 1450 | |||
| 1430 | // Add/subtract (shifted register) | 1451 | // Add/subtract (shifted register) |
| 1431 | 1452 | ||
| 1432 | pub fn addShiftedRegister( | 1453 | pub fn addShiftedRegister( |
test/behavior/basic.zig-2| ... | @@ -15,8 +15,6 @@ test "empty function with comments" { | ... | @@ -15,8 +15,6 @@ test "empty function with comments" { |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | test "truncate" { | 17 | test "truncate" { |
| 18 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 19 | |||
| 20 | try expect(testTruncate(0x10fd) == 0xfd); | 18 | try expect(testTruncate(0x10fd) == 0xfd); |
| 21 | comptime try expect(testTruncate(0x10fd) == 0xfd); | 19 | comptime try expect(testTruncate(0x10fd) == 0xfd); |
| 22 | } | 20 | } |