| ... | ... | @@ -321,6 +321,17 @@ pub const Instruction = union(enum) { |
| 321 | 321 | fixed: u6 = 0b011010, |
| 322 | 322 | sf: u1, |
| 323 | 323 | }, |
| 324 | conditional_select: struct { |
| 325 | rd: u5, |
| 326 | rn: u5, |
| 327 | op2: u2, |
| 328 | cond: u4, |
| 329 | rm: u5, |
| 330 | fixed: u8 = 0b11010100, |
| 331 | s: u1, |
| 332 | op: u1, |
| 333 | sf: u1, |
| 334 | }, |
| 324 | 335 | |
| 325 | 336 | pub const Shift = struct { |
| 326 | 337 | shift: Type = .lsl, |
| ... | ... | @@ -388,6 +399,57 @@ pub const Instruction = union(enum) { |
| 388 | 399 | /// Integer: Always |
| 389 | 400 | /// Floating point: Always |
| 390 | 401 | nv, |
| 402 | |
| 403 | /// Converts a std.math.CompareOperator into a condition flag, |
| 404 | /// i.e. returns the condition that is true iff the result of the |
| 405 | /// comparison is true. Assumes signed comparison |
| 406 | pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition { |
| 407 | return switch (op) { |
| 408 | .gte => .ge, |
| 409 | .gt => .gt, |
| 410 | .neq => .ne, |
| 411 | .lt => .lt, |
| 412 | .lte => .le, |
| 413 | .eq => .eq, |
| 414 | }; |
| 415 | } |
| 416 | |
| 417 | /// Converts a std.math.CompareOperator into a condition flag, |
| 418 | /// i.e. returns the condition that is true iff the result of the |
| 419 | /// comparison is true. Assumes unsigned comparison |
| 420 | pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition { |
| 421 | return switch (op) { |
| 422 | .gte => .cs, |
| 423 | .gt => .hi, |
| 424 | .neq => .ne, |
| 425 | .lt => .cc, |
| 426 | .lte => .ls, |
| 427 | .eq => .eq, |
| 428 | }; |
| 429 | } |
| 430 | |
| 431 | /// Returns the condition which is true iff the given condition is |
| 432 | /// false (if such a condition exists) |
| 433 | pub fn negate(cond: Condition) Condition { |
| 434 | return switch (cond) { |
| 435 | .eq => .ne, |
| 436 | .ne => .eq, |
| 437 | .cs => .cc, |
| 438 | .cc => .cs, |
| 439 | .mi => .pl, |
| 440 | .pl => .mi, |
| 441 | .vs => .vc, |
| 442 | .vc => .vs, |
| 443 | .hi => .ls, |
| 444 | .ls => .hi, |
| 445 | .ge => .lt, |
| 446 | .lt => .ge, |
| 447 | .gt => .le, |
| 448 | .le => .gt, |
| 449 | .al => unreachable, |
| 450 | .nv => unreachable, |
| 451 | }; |
| 452 | } |
| 391 | 453 | }; |
| 392 | 454 | |
| 393 | 455 | pub fn toU32(self: Instruction) u32 { |
| ... | ... | @@ -407,6 +469,7 @@ pub const Instruction = union(enum) { |
| 407 | 469 | // TODO once packed structs work, this can be refactored |
| 408 | 470 | .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25), |
| 409 | 471 | .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31), |
| 472 | .conditional_select => |v| @as(u32, v.rd) | @as(u32, v.rn) << 5 | @as(u32, v.op2) << 10 | @as(u32, v.cond) << 12 | @as(u32, v.rm) << 16 | @as(u32, v.fixed) << 21 | @as(u32, v.s) << 29 | @as(u32, v.op) << 30 | @as(u32, v.sf) << 31, |
| 410 | 473 | }; |
| 411 | 474 | } |
| 412 | 475 | |
| ... | ... | @@ -883,6 +946,33 @@ pub const Instruction = union(enum) { |
| 883 | 946 | }; |
| 884 | 947 | } |
| 885 | 948 | |
| 949 | fn conditionalSelect( |
| 950 | op2: u2, |
| 951 | op: u1, |
| 952 | s: u1, |
| 953 | rd: Register, |
| 954 | rn: Register, |
| 955 | rm: Register, |
| 956 | cond: Condition, |
| 957 | ) Instruction { |
| 958 | return Instruction{ |
| 959 | .conditional_select = .{ |
| 960 | .rd = rd.id(), |
| 961 | .rn = rn.id(), |
| 962 | .op2 = op2, |
| 963 | .cond = @enumToInt(cond), |
| 964 | .rm = rm.id(), |
| 965 | .s = s, |
| 966 | .op = op, |
| 967 | .sf = switch (rd.size()) { |
| 968 | 32 => 0b0, |
| 969 | 64 => 0b1, |
| 970 | else => unreachable, // unexpected register size |
| 971 | }, |
| 972 | }, |
| 973 | }; |
| 974 | } |
| 975 | |
| 886 | 976 | // Helper functions for assembly syntax functions |
| 887 | 977 | |
| 888 | 978 | // Move wide (immediate) |
| ... | ... | @@ -1154,6 +1244,24 @@ pub const Instruction = union(enum) { |
| 1154 | 1244 | pub fn cbnz(rt: Register, offset: i21) Instruction { |
| 1155 | 1245 | return compareAndBranch(0b1, rt, offset); |
| 1156 | 1246 | } |
| 1247 | |
| 1248 | // Conditional select |
| 1249 | |
| 1250 | pub fn csel(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction { |
| 1251 | return conditionalSelect(0b00, 0b0, 0b0, rd, rn, rm, cond); |
| 1252 | } |
| 1253 | |
| 1254 | pub fn csinc(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction { |
| 1255 | return conditionalSelect(0b01, 0b0, 0b0, rd, rn, rm, cond); |
| 1256 | } |
| 1257 | |
| 1258 | pub fn csinv(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction { |
| 1259 | return conditionalSelect(0b00, 0b1, 0b0, rd, rn, rm, cond); |
| 1260 | } |
| 1261 | |
| 1262 | pub fn csneg(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction { |
| 1263 | return conditionalSelect(0b01, 0b1, 0b0, rd, rn, rm, cond); |
| 1264 | } |
| 1157 | 1265 | }; |
| 1158 | 1266 | |
| 1159 | 1267 | test { |
| ... | ... | @@ -1319,6 +1427,10 @@ test "serialize instructions" { |
| 1319 | 1427 | .inst = Instruction.addShiftedRegister(.x0, .x1, .x2, .lsl, 5), |
| 1320 | 1428 | .expected = 0b1_0_0_01011_00_0_00010_000101_00001_00000, |
| 1321 | 1429 | }, |
| 1430 | .{ // csinc x1, x2, x4, eq |
| 1431 | .inst = Instruction.csinc(.x1, .x2, .x4, .eq), |
| 1432 | .expected = 0b1_0_0_11010100_00100_0000_0_1_00010_00001, |
| 1433 | }, |
| 1322 | 1434 | }; |
| 1323 | 1435 | |
| 1324 | 1436 | for (testcases) |case| { |