feat(asm): add prefetch hint encoding
This commit is contained in:
@@ -317,3 +317,37 @@ end:
|
|||||||
t.Errorf("jump-folding mismatch:\n got: %s\n want: %s", hexBytes(code), hexBytes(want))
|
t.Errorf("jump-folding mismatch:\n got: %s\n want: %s", hexBytes(code), hexBytes(want))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAssemblePrefetch(t *testing.T) {
|
||||||
|
fn := firstText(t, `
|
||||||
|
#include "textflag.h"
|
||||||
|
TEXT ·pf(SB), NOSPLIT, $0
|
||||||
|
PREFETCHNTA (AX)
|
||||||
|
PREFETCHT0 (BX)
|
||||||
|
PREFETCHT1 8(CX)
|
||||||
|
PREFETCHT2 -1(AX)(R12*1)
|
||||||
|
RET
|
||||||
|
`)
|
||||||
|
code, _, err := Assemble(fn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Assemble: %v", err)
|
||||||
|
}
|
||||||
|
got := strings.Join(disasm(t, code), "\n")
|
||||||
|
want := strings.Join([]string{
|
||||||
|
"prefetchnta zmmword ptr [rax]",
|
||||||
|
"prefetcht0 zmmword ptr [rbx]",
|
||||||
|
"prefetcht1 zmmword ptr [rcx+0x8]",
|
||||||
|
"prefetcht2 zmmword ptr [rax+r12-0x1]",
|
||||||
|
"ret",
|
||||||
|
}, "\n")
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("prefetch disassembly mismatch:\n got:\n%s\n want:\n%s", got, want)
|
||||||
|
}
|
||||||
|
// Byte-level expectations: 0F 18 with the variant in the reg field.
|
||||||
|
if hex := hexBytes(code[:3]); hex != "0f 18 00" {
|
||||||
|
t.Errorf("PREFETCHNTA bytes: got %s, want 0f 18 00", hex)
|
||||||
|
}
|
||||||
|
if hex := hexBytes(code[3:6]); hex != "0f 18 0b" {
|
||||||
|
t.Errorf("PREFETCHT0 bytes: got %s, want 0f 18 0b", hex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ func (e *enc) encode(mnem string, ops []Operand) error {
|
|||||||
return e.encodeCount(base, ops, size)
|
return e.encodeCount(base, ops, size)
|
||||||
case "BSWAP":
|
case "BSWAP":
|
||||||
return e.encodeBswap(ops, size)
|
return e.encodeBswap(ops, size)
|
||||||
|
case "PREFETCHNTA", "PREFETCHT0", "PREFETCHT1", "PREFETCHT2":
|
||||||
|
return e.encodePrefetch(base, ops)
|
||||||
case "MOVBLZX", "MOVBQZX", "MOVWLZX", "MOVWQZX", "MOVWLSX", "MOVLQSX":
|
case "MOVBLZX", "MOVBQZX", "MOVWLZX", "MOVWQZX", "MOVWLSX", "MOVLQSX":
|
||||||
return e.encodeMovExtend(base, ops)
|
return e.encodeMovExtend(base, ops)
|
||||||
case "CVTSL2SD", "CVTSQ2SD":
|
case "CVTSL2SD", "CVTSQ2SD":
|
||||||
@@ -124,6 +126,30 @@ func (e *enc) encode(mnem string, ops []Operand) error {
|
|||||||
return fmt.Errorf("unsupported instruction %q", mnem)
|
return fmt.Errorf("unsupported instruction %q", mnem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// encodePrefetch emits the 0F 18 /r prefetch hints: the reg field selects
|
||||||
|
// the locality (NTA=0, T0=1, T1=2, T2=3) and the single operand is memory.
|
||||||
|
func (e *enc) encodePrefetch(base string, ops []Operand) error {
|
||||||
|
if len(ops) != 1 {
|
||||||
|
return fmt.Errorf("%s expects one memory operand", base)
|
||||||
|
}
|
||||||
|
m, ok := ops[0].(Mem)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("%s requires a memory operand", base)
|
||||||
|
}
|
||||||
|
i := newInstr(0, []byte{0x0F, 0x18})
|
||||||
|
if err := setMem(i, prefetchVariant[base], m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return e.emit(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
var prefetchVariant = map[string]int{
|
||||||
|
"PREFETCHNTA": 0,
|
||||||
|
"PREFETCHT0": 1,
|
||||||
|
"PREFETCHT1": 2,
|
||||||
|
"PREFETCHT2": 3,
|
||||||
|
}
|
||||||
|
|
||||||
// splitSize separates a trailing B/W/L/Q size suffix from the mnemonic.
|
// splitSize separates a trailing B/W/L/Q size suffix from the mnemonic.
|
||||||
func splitSize(upper string) (base string, size int) {
|
func splitSize(upper string) (base string, size int) {
|
||||||
if upper == "" {
|
if upper == "" {
|
||||||
|
|||||||
Reference in New Issue
Block a user