diff --git a/asm/riscv_assemble.go b/asm/riscv_assemble.go index bd2e787..f95b3db 100644 --- a/asm/riscv_assemble.go +++ b/asm/riscv_assemble.go @@ -352,6 +352,26 @@ func riscvPadBytes(pad int) []byte { return out } +// riscvFenceFlags carries the toolchain's sixteen FENCE flag spellings (its +// specialOperands table between SPOP_FENCE_BEGIN and SPOP_FENCE_END), each +// with the 4-bit encoding the predecessor and successor fields pack. +var riscvFenceFlags = map[string]uint32{ + "W": 1, "R": 2, "RW": 3, "O": 4, "OW": 5, "OR": 6, "ORW": 7, + "I": 8, "IW": 9, "IR": 10, "IRW": 11, "IO": 12, "IOW": 13, + "IOR": 14, "IORW": 15, +} + +// riscvFenceFlag resolves one FENCE flag operand (a bare name such as W or +// IORW) to its 4-bit encoding. +func riscvFenceFlag(op *ast.Operand) (uint32, bool) { + name := "" + if op.Addr.Sym != nil { + name = op.Addr.Sym.Name + } + v, ok := riscvFenceFlags[strings.ToUpper(name)] + return v, ok +} + // riscvInstrSize returns the encoded size in bytes of a RISC-V instruction. // Most instructions are 4 bytes; MOV with a large immediate and I-type // arithmetic with a large immediate expand to several (possibly compressed) @@ -1322,6 +1342,19 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if mnem == "PAUSE" { imm = 0x010 } + // FENCE pred, succ spells both flags with the toolchain's sixteen + // IORW combinations, packed as pred<<4 | succ in the immediate. + if mnem == "FENCE" && len(ops) == 2 { + pred, ok := riscvFenceFlag(ops[0]) + if !ok { + return nil, fmt.Errorf("FENCE: invalid predecessor operand %q", ops[0].Raw) + } + succ, ok := riscvFenceFlag(ops[1]) + if !ok { + return nil, fmt.Errorf("FENCE: invalid successor operand %q", ops[1].Raw) + } + imm = int32(pred)<<4 | int32(succ) + } word = riscvIType(enc, 0, 0, imm) return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil