| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| | 2 | const mem = std.mem; |
| 2 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 3 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 4 | | 5 | |
| ... | @@ -360,7 +361,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -360,7 +361,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 360 | .error_set_has_value, | 361 | .error_set_has_value, |
| 361 | .frame_addr, | 362 | .frame_addr, |
| 362 | | 363 | |
| 363 | .assembly, | | |
| 364 | .is_err_ptr, | 364 | .is_err_ptr, |
| 365 | .is_non_err_ptr, | 365 | .is_non_err_ptr, |
| 366 | | 366 | |
| ... | @@ -434,6 +434,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -434,6 +434,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 434 | => |tag| return cg.fail("TODO: implement spork8 inst: {s}", .{@tagName(tag)}), | 434 | => |tag| return cg.fail("TODO: implement spork8 inst: {s}", .{@tagName(tag)}), |
| 435 | | 435 | |
| 436 | .unreach => cg.airUnreachable(inst), | 436 | .unreach => cg.airUnreachable(inst), |
| | 437 | .assembly => cg.airAssembly(inst), |
| 437 | .trap => cg.airTrap(inst), | 438 | .trap => cg.airTrap(inst), |
| 438 | | 439 | |
| 439 | .work_item_id, | 440 | .work_item_id, |
| ... | @@ -453,6 +454,77 @@ fn airTrap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -453,6 +454,77 @@ fn airTrap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 453 | try cg.addTag(.halt); | 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 | pub fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void { | 528 | pub fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void { |
| 457 | try cg.mir_instructions.append(cg.gpa, inst); | 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,6 +533,10 @@ pub fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void { |
| 461 | try cg.addInst(.{ .tag = tag, .data = .{ .nothing = {} } }); | 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 | fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { | 540 | fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { |
| 465 | const zcu = cg.pt.zcu; | 541 | const zcu = cg.pt.zcu; |
| 466 | const func = zcu.funcInfo(cg.func_index); | 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,3 +546,74 @@ fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemor |
| 470 | fn extraLen(cg: *const CodeGen) u32 { | 546 | fn extraLen(cg: *const CodeGen) u32 { |
| 471 | return @intCast(cg.mir_extra.items.len - cg.start_mir_extra_off); | 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 | }; |