in x86/x86map/map.go [1124:1342]
func printDecoderPass(p *Prog, pc int, printing bool, ops map[string]bool) int {
// Record PC on first pass.
if p.PC == 0 {
p.PC = pc
}
// If PC doesn't match, we've already printed this code
// because it was reached some other way. Jump to that copy.
if p.PC != pc {
if printing {
fmt.Printf("/*%d*/\tuint16(xJump), %d,\n", pc, p.PC)
}
return pc + 2
}
// Otherwise, emit the code for the given action.
// At the bottom, if next is non-nil, emit code for next.
// Then emit the code for the children named by the keys.
keys := p.keys()
var next *Prog
switch p.Action {
default:
log.Printf("printDecoderPass: unknown action %q: %s", p.Action, p.Path)
case "decode":
// Decode hex bytes or /n modrm op checks.
// Hex bytes take priority, so do them first.
// Hex bytes of the form "40+" indicate an
// 8 entry-wide swath of codes: 40, 41, ..., 47.
hex := 0
slash := 0
for _, key := range keys {
if isHex(key) {
if strings.Contains(key, "+") {
hex += 8
} else {
hex++
}
}
if isSlashNum(key) {
slash++
}
}
if hex > 0 {
// TODO(rsc): Introduce an xCondByte256 that has 256 child entries
// and no explicit keys. That will cut the size in half for large
// tables.
if printing {
fmt.Printf("/*%d*/\tuint16(xCondByte), %d,\n", pc, hex)
for _, key := range keys {
if !isHex(key) {
continue
}
if i := strings.Index(key, "+"); i >= 0 {
nextPC := p.Child[key].PC
n, _ := strconv.ParseUint(key[:i], 16, 0)
for j := 0; j < 8; j++ {
fmt.Printf("\t%#02x, %d,\n", int(n)+j, nextPC)
}
continue
}
fmt.Printf("\t0x%s, %d,\n", key, p.Child[key].PC)
}
}
pc += 2 + 2*hex
// All other condition checks fail the decoding if nothing is found,
// but this one falls through so that we can then do /n checks.
// If there are no upcoming /n checks, insert an explicit failure.
if slash == 0 {
if printing {
fmt.Printf("\tuint16(xFail),\n")
}
pc++
}
}
if slash > 0 {
if printing {
fmt.Printf("/*%d*/\tuint16(xCondSlashR),\n", pc)
for i := 0; i < 8; i++ {
fmt.Printf("\t%d, // %d\n", p.childPC(fmt.Sprintf("/%d", i)), i)
}
}
pc += 1 + 8
}
case "is64":
// Decode based on processor mode: 64-bit or not.
if len(keys) == 1 && keys[0] == "any" {
next = p.Child["any"]
break
}
if p.Child["any"] != nil {
log.Printf("%s: mixed is64 keys: %v", p.Path, keys)
}
if printing {
fmt.Printf("/*%d*/\tuint16(xCondIs64), %d, %d,\n", pc, p.childPC("0"), p.childPC("1"))
}
pc += 3
case "prefix":
// Decode based on presence of prefix.
// The "0" prefix means "none of the above", so if there's
// nothing else, it's the same as "any".
if len(keys) == 1 && (keys[0] == "any" || keys[0] == "0") {
next = p.Child["any"]
break
}
if p.Child["any"] != nil {
log.Printf("%s: mixed prefix keys: %v", p.Path, keys)
}
// Emit the prefixes in reverse sorted order, so that F3 and F2 are
// considered before 66, and the fallback 0 is considered last.
if printing {
fmt.Printf("/*%d*/\tuint16(xCondPrefix), %d,\n", pc, len(keys))
for i := len(keys) - 1; i >= 0; i-- {
key := keys[i]
nextPC := p.Child[key].PC
fmt.Printf("\t0x%s, %d,\n", key, nextPC)
}
}
pc += 2 + 2*len(keys)
case "addrsize":
// Decode based on address size attribute.
if len(keys) == 1 && keys[0] == "any" {
next = p.Child["any"]
break
}
if p.Child["any"] != nil {
log.Printf("%s: mixed addrsize keys: %v", p.Path, keys)
}
if printing {
fmt.Printf("/*%d*/\tuint16(xCondAddrSize), %d, %d, %d,\n", pc, p.childPC("16"), p.childPC("32"), p.childPC("64"))
}
pc += 4
case "datasize":
// Decode based on operand size attribute.
if len(keys) == 1 && keys[0] == "any" {
next = p.Child["any"]
break
}
if p.Child["any"] != nil {
log.Printf("%s: mixed datasize keys: %v", p.Path, keys)
}
if printing {
fmt.Printf("/*%d*/\tuint16(xCondDataSize), %d, %d, %d,\n", pc, p.childPC("16"), p.childPC("32"), p.childPC("64"))
}
pc += 4
case "ismem":
// Decode based on modrm form: memory or register reference.
if len(keys) == 1 && keys[0] == "any" {
next = p.Child["any"]
break
}
if p.Child["any"] != nil {
log.Printf("%s: mixed ismem keys: %v", p.Path, keys)
}
if printing {
fmt.Printf("/*%d*/\tuint16(xCondIsMem), %d, %d,\n", pc, p.childPC("0"), p.childPC("1"))
}
pc += 3
case "op":
// Set opcode.
ops[keys[0]] = true
if printing {
fmt.Printf("/*%d*/\tuint16(xSetOp), uint16(%s),\n", pc, keys[0])
}
next = p.Child[keys[0]]
pc += 2
case "read":
// Read argument bytes.
if printing {
fmt.Printf("/*%d*/\tuint16(xRead%s),\n", pc, xOp(keys[0]))
}
next = p.Child[keys[0]]
pc++
case "arg":
// Record instruction argument (interpret bytes loaded with read).
if printing {
fmt.Printf("/*%d*/\tuint16(xArg%s),\n", pc, xOp(keys[0]))
}
next = p.Child[keys[0]]
pc++
case "match":
// Finish match.
if printing {
fmt.Printf("/*%d*/\tuint16(xMatch),\n", pc)
}
pc++
return pc
}
if next != nil {
pc = printDecoderPass(next, pc, printing, ops)
}
for _, key := range keys {
q := p.Child[key]
if q.PC == 0 || q.PC == pc {
pc = printDecoderPass(q, pc, printing, ops)
}
}
return pc
}