| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; |
| 2 | 3 | const Allocator = std.mem.Allocator; |
| 3 | 4 | const assert = std.debug.assert; |
| 4 | 5 | |
| ... | ... | @@ -360,7 +361,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 360 | 361 | .error_set_has_value, |
| 361 | 362 | .frame_addr, |
| 362 | 363 | |
| 363 | | .assembly, |
| 364 | 364 | .is_err_ptr, |
| 365 | 365 | .is_non_err_ptr, |
| 366 | 366 | |
| ... | ... | @@ -434,6 +434,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 434 | 434 | => |tag| return cg.fail("TODO: implement spork8 inst: {s}", .{@tagName(tag)}), |
| 435 | 435 | |
| 436 | 436 | .unreach => cg.airUnreachable(inst), |
| 437 | .assembly => cg.airAssembly(inst), |
| 437 | 438 | .trap => cg.airTrap(inst), |
| 438 | 439 | |
| 439 | 440 | .work_item_id, |
| ... | ... | @@ -453,6 +454,77 @@ fn airTrap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 453 | 454 | try cg.addTag(.halt); |
| 454 | 455 | } |
| 455 | 456 | |
| 457 | fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 458 | const unwrapped_asm = cg.air.unwrapAsm(inst); |
| 459 | const outputs = unwrapped_asm.outputs; |
| 460 | // const inputs = unwrapped_asm.inputs; |
| 461 | |
| 462 | const zcu = cg.pt.zcu; |
| 463 | // const output_ty = cg.typeOfIndex(inst); |
| 464 | |
| 465 | if (outputs.len != 0) { |
| 466 | @panic("TODO: Support assembly outputs"); |
| 467 | } |
| 468 | |
| 469 | var constValues: std.array_hash_map.String(u8) = .empty; |
| 470 | defer constValues.deinit(zcu.gpa); |
| 471 | { |
| 472 | var it = unwrapped_asm.iterateInputs(); |
| 473 | while (it.next()) |input| { |
| 474 | const constraint = input.constraint; |
| 475 | if (!mem.eql(u8, constraint, "I")) { |
| 476 | return cg.fail("Assembly constraint {q} not supported", .{constraint}); |
| 477 | } |
| 478 | const operand = input.operand.toInterned() orelse { |
| 479 | return cg.fail("Immediate argument to inline assembly must be compile-time value", .{}); |
| 480 | }; |
| 481 | const name = input.name; |
| 482 | |
| 483 | const value = switch (zcu.intern_pool.indexToKey(operand)) { |
| 484 | .int => |val| v: { |
| 485 | if (val.ty != .u8_type) { |
| 486 | return cg.fail("Non-u8 type used in inline assembly value: {}", .{val.ty}); |
| 487 | } |
| 488 | break :v val.storage.u64; |
| 489 | }, |
| 490 | else => return cg.fail("Non-int operands not supported", .{}), |
| 491 | }; |
| 492 | |
| 493 | try constValues.put(zcu.gpa, name, @intCast(value)); |
| 494 | |
| 495 | // return cg.fail("TODO: Constraint={q}, name={q}, value={}", .{ constraint, name, value }); |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | { |
| 500 | var lines = mem.tokenizeScalar(u8, unwrapped_asm.source, '\n'); |
| 501 | while (lines.next()) |line| { |
| 502 | var tokens = mem.tokenizeScalar(u8, line, ' '); |
| 503 | const op = tokens.next().?; |
| 504 | const instType = std.meta.stringToEnum(AsmInstType, op) orelse return cg.fail("Invalid asm instruction: {q}", .{op}); |
| 505 | switch (instType) { |
| 506 | .LoadI => { |
| 507 | const register = std.meta.stringToEnum(Register, tokens.next().?).?; |
| 508 | const value = tokens.next().?; |
| 509 | const intValue = v: { |
| 510 | if (mem.startsWith(u8, value, "%[")) { |
| 511 | const name = value[2 .. value.len - 1]; |
| 512 | break :v constValues.get(name).?; |
| 513 | } else { |
| 514 | break :v std.fmt.parseInt(u8, value, 0) catch unreachable; |
| 515 | } |
| 516 | }; |
| 517 | if (register != .OutA) { |
| 518 | return cg.fail("TODO: other variants of LoadI", .{}); |
| 519 | } |
| 520 | try cg.addTagImm8(.load_i_outa, intValue); |
| 521 | }, |
| 522 | else => return cg.fail("TODO: support asm instruction: {t}", .{instType}), |
| 523 | } |
| 524 | } |
| 525 | } |
| 526 | } |
| 527 | |
| 456 | 528 | pub fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void { |
| 457 | 529 | try cg.mir_instructions.append(cg.gpa, inst); |
| 458 | 530 | } |
| ... | ... | @@ -461,6 +533,10 @@ pub fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void { |
| 461 | 533 | try cg.addInst(.{ .tag = tag, .data = .{ .nothing = {} } }); |
| 462 | 534 | } |
| 463 | 535 | |
| 536 | pub fn addTagImm8(cg: *CodeGen, tag: Mir.Inst.Tag, imm8: u8) error{OutOfMemory}!void { |
| 537 | try cg.addInst(.{ .tag = tag, .data = .{ .imm8 = imm8 } }); |
| 538 | } |
| 539 | |
| 464 | 540 | fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { |
| 465 | 541 | const zcu = cg.pt.zcu; |
| 466 | 542 | const func = zcu.funcInfo(cg.func_index); |
| ... | ... | @@ -470,3 +546,74 @@ fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemor |
| 470 | 546 | fn extraLen(cg: *const CodeGen) u32 { |
| 471 | 547 | return @intCast(cg.mir_extra.items.len - cg.start_mir_extra_off); |
| 472 | 548 | } |
| 549 | |
| 550 | const AsmInstType = enum(u8) { |
| 551 | SetPageReg, // Set the memory address high byte to a register value. |
| 552 | SetPageI, // Set the memory address high byte to a constant value. |
| 553 | SetAddrReg, // Set the memory address low byte to a register value. |
| 554 | SetAddrI, // Set the memory address low byte to a constant value. |
| 555 | Load, // Load a value from a constant address into a register. |
| 556 | LoadI, // Load a constant value into a register. |
| 557 | LoadP, // Load a value from a constant address (setting low byte only) into a register. |
| 558 | LoadInc, // Load a value from the currently set memory address into a register, and increment the address n times. |
| 559 | LoadStck, // Load a value from an offset on the current stack frame into a register. |
| 560 | Store, // Store a value to a constant address from a register. |
| 561 | StoreI, // Store a constant value into a constant address. |
| 562 | StoreP, // Store a value to a constant address (low byte only) from a register. |
| 563 | StoreInc, // Store a value from the currently set memory address from a register, and increment the address n times. |
| 564 | StoreStck, // Store a value to an offset on the current stack frame, from a register. |
| 565 | StoreNStck, // Store a value to an offset on the next stack frame, from a register. |
| 566 | StorePStck, // Store a value to an offset on the previous stack frame, from a register. |
| 567 | StoreStckI, // Store a constant value to an offset on the current stack frame. |
| 568 | StoreNStckI, // Store a constant value to an offset on the next stack frame. |
| 569 | StorePStckI, // Store a constant value to an offset on the previous stack frame. |
| 570 | Copy, // Copy a value from one register to another register. |
| 571 | Jump, // Jump to a constant location. |
| 572 | JumpReg, // Jump to a register A (high byte) + register B (low byte). |
| 573 | JumpMem, // Jump to a location pointed to by memory at the current memory address (high byte first). |
| 574 | Call, // Call a function. |
| 575 | Return, // Return from a function. |
| 576 | CmpI, // Compare A to a constant value (sets flags, but discards result). |
| 577 | CmpAndI, // Compare A to a constant value with bitwise AND (sets flags, but discards result). |
| 578 | Cmp, // Compare A to a value from memory (sets flags, but discards result). |
| 579 | CmpAnd, // Compare A to a value in memory with bitwise AND (sets flags, but discards result). |
| 580 | CmpReg, // Compare A to a value from a register (sets flags, but discards result). |
| 581 | CmpAndReg, // Compare A to a value from a register with bitwise AND (sets flags, but discards result). |
| 582 | ShiftL, // Shift B left by 1. |
| 583 | ShiftR, // Shift B right by 1. |
| 584 | RotateL, // Rotate B left by 1. |
| 585 | RotateR, // Rotate B right by 1. |
| 586 | AddI, // Add a constant value to A. |
| 587 | SubI, // Subtract a constant value from A. |
| 588 | AndI, // Bitwise-AND A with a constant value. |
| 589 | AddINF, // Add a constant value to A, without updating flags. |
| 590 | SubINF, // Subtract a constant value from A, without updating flags. |
| 591 | AndINF, // Bitwise-AND A with a constant value, without updating flags. |
| 592 | AccumulateAdd, // Add register B to A -> A. |
| 593 | AccumulateSub, // Subtract register B from A -> A. |
| 594 | AccumulateAnd, // A & B -> A. |
| 595 | OrI, // Bitwise OR B with A -> A. |
| 596 | XorI, // Bitwise OR a constant value with A -> A. |
| 597 | Not, // Invert register A. |
| 598 | Add, // Add a value from memory to A. |
| 599 | Sub, // Subtract a value from memory from A. |
| 600 | And, // AND A with a value from memory. |
| 601 | Or, // OR A with a value from memory. |
| 602 | Xor, // XOR A with a value from memory. |
| 603 | Nop, // No-op. |
| 604 | Nop1, // No-op with 1 extra clock cycle. |
| 605 | Nop2, // No-op with 2 extra clock cycles. |
| 606 | Halt, // Halt - stop the program forever (until reset). |
| 607 | }; |
| 608 | |
| 609 | const Register = enum(u8) { |
| 610 | A, |
| 611 | B, |
| 612 | C, |
| 613 | PCnt, |
| 614 | MAdr, |
| 615 | Stack, |
| 616 | OutA, |
| 617 | Shift, |
| 618 | Swap, |
| 619 | }; |