2 điểm bởi GN⁺ 2024-10-07 | 1 bình luận | Chia sẻ qua WhatsApp
  • vpternlogd của AVX-512 là một lệnh SIMD cho phép chọn logic Boolean bitwise bất kỳ với ba đầu vào bằng một giá trị tức thời 8 bit và thực thi trong một lần
  • Điểm cốt lõi không phải là ghi nhớ các biểu thức logic phức tạp, mà là xem #imm8 như một bảng tra cứu cho 8 trường hợp của ba đầu vào A/B/C
  • Blitter của Amiga năm 1985 cũng chỉ định tổ hợp logic theo cùng cách, với ba nguồn bitmap và một minterm 8 bit; với masked sprite, 0xE2 thường được dùng
  • Nếu đọc 8 bit của cột kết quả từ dưới lên trên, ta sẽ có giá trị tức thời, nên các điều kiện như “chính xác hai trong ba đầu vào là 1” cũng có thể tạo trực tiếp bằng 0x68
  • Ví dụ vpternlogd trong tài liệu Intel và 0xE2 quen thuộc của demoscene Amiga giao nhau, cho thấy lệnh SIMD hiện đại và phần cứng đồ họa retro có chung một cảm quan thiết kế

vpternlogd của AVX-512

  • vpternlogd là một lệnh logic tam phân bitwise nổi bật trong tài liệu trình bày thiết kế ISA AVX-512
  • Nhận ba đầu vào A, B, C và có thể biểu diễn logic Boolean bất kỳ bằng một lệnh duy nhất
    • Ví dụ: (NOT A) OR ((NOT B) XOR (C AND A))
  • Đầu vào có thể là thanh ghi 512 bit, nên có thể áp dụng logic phức tạp đồng thời lên toàn bộ 512 bit
  • Thay vì liên tục thêm các lệnh chuyên dụng như foo_and_a_or_not_b, nó xử lý nhiều kiểu logic bằng một lệnh linh hoạt và một giá trị tức thời 8 bit
VPTERNLOGD r0, r1, r3, #imm8
  • #imm8 quyết định hàm logic bit nào sẽ được thực hiện
  • Nhiều tài liệu chỉ giải thích rằng giá trị tức thời “quyết định một hàm nhị phân cụ thể”, khiến cách tính thực tế khó nắm bắt một cách trực quan

Blitter Amiga và minterm

  • Máy tính thập niên 1980 thường dùng chip tùy biến để xử lý đồ họa
  • Blitter của Commodore Amiga 500 di chuyển đồ họa bitmap từ vị trí này sang vị trí khác, đồng thời áp dụng phép toán logic
  • Nó xử lý tối đa ba nguồn bitmap, và phép toán logic giữa chúng được chỉ định bằng minterm, một giá trị 8 bit
  • Về việc chọn tổ hợp logic bằng ba nguồn và một giá trị 8 bit, nó có cấu trúc giống vpternlogd
  • Nhiều lập trình viên Amiga thường tái sử dụng các giá trị hay dùng thay vì tự hiểu cách tính minterm
    • Khi xóa bộ đệm dùng 0x00
    • Khi vẽ masked sprite dùng 0xE2
  • “Amiga Hardware Reference Manual” năm 1989 giải thích cách tính minterm bằng các ký hiệu rối rắm, nên không giúp ích nhiều cho những người làm demo thời đó

Tính giá trị 8 bit như một bảng tra cứu

  • #imm8 hoặc minterm có thể được xem không phải là tổ hợp các toán tử logic, mà là một bảng tra cứu gồm 8 mục
  • Ba đầu vào A, B, C mỗi đầu vào có thể là 0 hoặc 1, nên có 8 tổ hợp khả dĩ
  • Chỉ cần điền trực tiếp kết quả mong muốn vào cột thứ tư
A B C Kết quả mong muốn
0 0 0 ?
0 0 1 ?
0 1 0 ?
0 1 1 ?
1 0 0 ?
1 0 1 ?
1 1 0 ?
1 1 1 ?
  • Ví dụ, nếu muốn kết quả là 1 khi chính xác hai trong ba đầu vào là 1, hãy điền các giá trị thỏa điều kiện đó vào cột thứ tư
  • Đọc 8 bit của cột kết quả từ dưới lên trên sẽ được 01101000, tức 0x68
  • Vì vậy, hàm 0x68 đặt kết quả thành 1 khi chính xác hai trong ba đầu vào là 1
  • Theo cùng cách, có thể lấy giá trị #imm8 cần thiết cho bất kỳ hàm logic nào giữa ba nguồn

0xE2 của masked sprite

  • Một trong những giá trị minterm rất phổ biến trên Amiga là 0xE2
  • Giá trị này thường được dùng khi render masked 2D sprite
    • A: bitmap sprite
    • B: mask sprite
    • C: background
  • Điều kiện có thể được diễn đạt bằng logic chương trình đơn giản
    • Nếu pixel mask B được đặt, kết quả là sprite A
    • Nếu pixel mask B không được đặt, kết quả là background C
A B C Kết quả mong muốn
0 0 0 0
0 0 1 1
0 1 0 0
0 1 1 0
1 0 0 0
1 0 1 1
1 1 0 1
1 1 1 1
  • Đọc cột kết quả từ dưới lên trên sẽ được 11100010, tức 0xE2
  • 0xE2 là một giá trị minterm rất quen thuộc trong văn hóa demoscene Amiga

Mối liên hệ tình cờ với tài liệu Intel

1 bình luận

 
GN⁺ 2024-10-07
Các ý kiến trên Hacker News
  • Có một cách đơn giản để lấy giá trị tức thời từ biểu thức muốn tính. Ví dụ nếu muốn tính (NOT A) OR ((NOT B) XOR (C AND A)) thì có thể viết như ~_MM_TERNLOG_A | (~_MM_TERNLOG_B ^ (_MM_TERNLOG_C & _MM_TERNLOG_A))
    Đúng nghĩa là biểu thức muốn tính, và nó được đánh giá thành giá trị tức thời từ các hằng _MM_TERNLOG_A/B/C được định nghĩa trong header intrinsic của gcc và clang: typedef enum { _MM_TERNLOG_A = 0xF0, _MM_TERNLOG_B = 0xCC, _MM_TERNLOG_C = 0xAA } _MM_TERNLOG_ENUM;
    Với MSVC thì có thể tự định nghĩa

    • Nếu không muốn trông như ma thuật, hãy viết bằng nhị phân: A = 0b11110000, B = 0b11001100, C = 0b10101010
    • Tài liệu hướng dẫn Amiga khuyên nên chuẩn hóa về dạng chuẩn tổng các tích
  • Chỉ nhìn tiêu đề, tôi tưởng ý là lệnh này không hoạt động đúng. Bài viết thực ra chỉ giải thích cách nó hoạt động

    • Tôi hiểu “busted” ở đây theo nghĩa là đã bắt quả tang người của Intel là fan Amiga. Có vẻ có gì đó bị mất khi chuyển từ tiếng Pháp, tiếng mẹ đẻ của tác giả, sang tiếng Anh
  • Tôi thời thiếu niên không viết “CRAP!” lên trang đó trong tài liệu phần cứng, nhưng đã nhìn chằm chằm vào nó rất lâu để cố hiểu
    Cuối cùng, như hầu hết mọi người, tôi chỉ tìm Bobs và BLTCON0 để sao chép đơn giản rồi giả vờ như chưa từng thấy phần đó
    Dù vậy vài năm sau tôi đạt A+ môn logic tính toán ở đại học, nên có lẽ sang chấn đó cũng giúp được phần nào

  • Nói về tiêu đề, “ternary logic” thường có nghĩa là logic có ba giá trị chân lý. Nhưng bài này nói về một lệnh compiler xử lý mọi cổng logic nhị phân nhận ba đầu vào

    • Tên lệnh x86 là ternlog và intrinsic cũng là ternarylogic, nên dù hơi tiếc, tiêu đề vẫn phù hợp
      Hơn nữa bitwise phần nào đã giúp phân biệt với “logic ba giá trị”, còn ternary cũng rất thường được dùng với nghĩa ba đầu vào. a ? b : c cũng thường được gọi là toán tử ba ngôi, và thực tế ternlog có thể mô phỏng phép toán ba ngôi này; bài viết cũng bàn đúng nội dung đó
    • Gần với “lệnh logic có tính ba ngôi” hơn là “lệnh logic tam trị”
    • Tôi không rõ logic có ba giá trị chân lý là gì. Có lẽ bạn đang nghĩ đến trinary chứ không phải ternary
      Từ góc nhìn C++, JavaScript, Python, cách dùng phổ biến nhất có vẻ là biểu thức ba ngôi dạng (a < b) ? 5 : 2. https://www.programiz.com/cpp-programming/ternary-operator
      Dù sao thì giả định nào cũng không quá quan trọng. Từ hoặc cụm từ vốn có nhiều nghĩa, và ternary nghĩa là gồm ba phần, nên cũng đúng ở đây
    • ternary ở đây không có nghĩa đó
      Trong C, + nhận hai đầu vào nên là toán tử nhị phân, còn ?: nhận ba đầu vào nên là toán tử ba ngôi. Chỉ vì nó là toán tử duy nhất trong C nên thường được gọi là “the ternary operator”, chứ bản chất không có gì đặc biệt
      vpternlogd triển khai mọi toán tử ba ngôi theo bit nhận ba đầu vào
    • Tôi cũng thấy phần này dễ gây nhầm lẫn. Tuy nhiên tên “đánh giá biểu thức nhị phân gồm ba hạng” thì kém súc tích hơn
  • Tôi tự hỏi có giống hàm BitBlt của Windows không. Ít nhất hình như nó đã có từ Windows 3.1, và tham số op quyết định cách kết hợp nguồn, đích và mặt nạ
    Tôi nhớ có các tên mã như BLACKNESS, tạo màu đen bất kể đầu vào, và COPY, sao chép nguồn sang đích. BLACKNESSWHITENESS nghe có nét thơ kỳ lạ
    Theo những gì tôi nhớ từ Petzold, nó được triển khai bằng phần mềm, nhưng khi gọi thì opcode được chuyển thành assembly tùy chỉnh bên trong hàm, nên có vẻ là một trường hợp mã tự sửa đổi hiếm trong hệ điều hành Windows

    • Đúng vậy. BitBlt ban đầu dùng các “operation codes” 16-bit phức tạp, lưu phép toán nhị phân bằng ký pháp Ba Lan ngược
      Sau này họ thêm “operation index” chứa cùng thông tin trong 1 byte như Amiga, ngắn hơn và thanh nhã hơn. Hiện nay mỗi raster operation code chứa cả operation index lẫn operation code, nên phần mã hóa bị trùng lặp. https://devblogs.microsoft.com/oldnewthing/20180528-00/?p=98...
  • Giống cách FPGA triển khai hàm logic tùy ý bằng bảng tra cứu

    • Về cơ bản CPU, GPU, FPGA đều hội tụ về cùng một dạng tiến hóa của cua trong tính toán. Chúng có các vùng tối ưu hóa khác nhau nhưng phơi bày cùng năng lực
    • Mọi logic đều có thể được triển khai bằng bộ nhớ, và mọi bộ nhớ đều có thể được triển khai bằng logic với một dạng cấu hình hồi tiếp nào đó
      Chỉ là ngoài các mục đích chuyên biệt như FPGA hoặc lệnh trong bài này, thường người ta không làm vậy. Thanh ghi tốc độ cao và RAM tĩnh đôi khi được làm bằng logic, nhưng thường thì tạo trực tiếp bằng transistor hơn là bằng cổng
    • Đa số là vậy nhưng không phải tất cả. Actel/Microsemi dùng cây multiplexer và cổng nhỏ
    • Bộ số học logic 74181 cũng làm như vậy
  • Vào https://www.sandpile.org và tìm VPTERNLOG trên trang opcode 3 byte https://www.sandpile.org/x86/opc_3.htm, bạn cũng có thể thấy AVX512BITALG2, một biến thể masking byte/word có vẻ từng được Intel lên kế hoạch
    Ngoài ra, từ toán hạng Ib có liên kết đến trang bảng chân trị ba ngôi chứa đủ 256 trường hợp: https://www.sandpile.org/x86/ternlog.htm

  • Việc tài liệu chọn hàm E2 làm ví dụ có thể là vì trong các hàm boolean ba đầu vào, đó gần như là mux cơ bản và chuẩn nhất: nếu B thì A, nếu không thì C
    Đây là hàm phổ dụng, nên không nhất thiết phải là fan Amiga mới chọn. Tất nhiên, thực tế cũng có thể họ là fan

  • Một ví dụ khác về việc nhét phép toán theo bit vào số nguyên là mã ROP của GDI trong Win32: https://learn.microsoft.com/en-us/windows/win32/gdi/ternary-...

  • Tôi không có tài liệu phần cứng Amiga chính thức, mà xem cuốn “Mapping the Amiga”. Cuốn này giải thích cùng nội dung đó dài dòng hơn một chút
    Tôi không nhớ lúc đó đã dùng minterm nào, nhưng nhờ cuốn này mà hình như tôi đã xoay xở triển khai được những thứ như shadebobs, bobs, vẽ đường 3D bằng XOR
    Trang tương ứng trong Mapping the Amiga: https://archive.org/details/1993-thomson-randy-rhett-anderso...