blob: 3aca0074d9060319e0afab1f1d7e179baf4ad683 [file] [log] [blame]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package loong64asm
import (
"encoding/binary"
"fmt"
)
type instArgs [5]instArg
// An instFormat describes the format of an instruction encoding.
type instFormat struct {
mask uint32
value uint32
op Op
// args describe how to decode the instruction arguments.
// args is stored as a fixed-size array.
// if there are fewer than len(args) arguments, args[i] == 0 marks
// the end of the argument list.
args instArgs
}
var (
errShort = fmt.Errorf("truncated instruction")
errUnknown = fmt.Errorf("unknown instruction")
)
var decoderCover []bool
func init() {
decoderCover = make([]bool, len(instFormats))
}
// Decode decodes the 4 bytes in src as a single instruction.
func Decode(src []byte) (inst Inst, err error) {
if len(src) < 4 {
return Inst{}, errShort
}
x := binary.LittleEndian.Uint32(src)
Search:
for i := range instFormats {
f := &instFormats[i]
if (x & f.mask) != f.value {
continue
}
// Decode args.
var args Args
for j, aop := range f.args {
if aop == 0 {
break
}
arg := decodeArg(aop, x, i)
if arg == nil {
// Cannot decode argument
continue Search
}
args[j] = arg
}
decoderCover[i] = true
inst = Inst{
Op: f.op,
Args: args,
Enc: x,
}
return inst, nil
}
return Inst{}, errUnknown
}
// decodeArg decodes the arg described by aop from the instruction bits x.
// It returns nil if x cannot be decoded according to aop.
func decodeArg(aop instArg, x uint32, index int) Arg {
switch aop {
case arg_fd:
return F0 + Reg(x&((1<<5)-1))
case arg_fj:
return F0 + Reg((x>>5)&((1<<5)-1))
case arg_fk:
return F0 + Reg((x>>10)&((1<<5)-1))
case arg_fa:
return F0 + Reg((x>>15)&((1<<5)-1))
case arg_rd:
return R0 + Reg(x&((1<<5)-1))
case arg_rj:
return R0 + Reg((x>>5)&((1<<5)-1))
case arg_rk:
return R0 + Reg((x>>10)&((1<<5)-1))
case arg_fcsr_4_0:
return FCSR0 + Fcsr(x&((1<<5)-1))
case arg_fcsr_9_5:
return FCSR0 + Fcsr((x>>5)&((1<<5)-1))
case arg_cd:
return FCC0 + Fcc(x&((1<<3)-1))
case arg_cj:
return FCC0 + Fcc((x>>5)&((1<<3)-1))
case arg_ca:
return FCC0 + Fcc((x>>15)&((1<<3)-1))
case arg_op_4_0:
tmp := x & ((1 << 5) - 1)
return Uimm{tmp, false}
case arg_csr_23_10:
tmp := (x >> 10) & ((1 << 14) - 1)
return Uimm{tmp, false}
case arg_sa2_16_15:
f := &instFormats[index]
tmp := SaSimm((x >> 15) & ((1 << 2) - 1))
if (f.op == ALSL_D) || (f.op == ALSL_W) || (f.op == ALSL_WU) {
return tmp + 1
} else {
return tmp + 0
}
case arg_sa3_17_15:
return SaSimm((x >> 15) & ((1 << 3) - 1))
case arg_code_4_0:
return CodeSimm(x & ((1 << 5) - 1))
case arg_code_14_0:
return CodeSimm(x & ((1 << 15) - 1))
case arg_ui5_14_10:
tmp := (x >> 10) & ((1 << 5) - 1)
return Uimm{tmp, false}
case arg_ui6_15_10:
tmp := (x >> 10) & ((1 << 6) - 1)
return Uimm{tmp, false}
case arg_ui12_21_10:
tmp := ((x >> 10) & ((1 << 12) - 1) & 0xfff)
return Uimm{tmp, false}
case arg_lsbw:
tmp := (x >> 10) & ((1 << 5) - 1)
return Uimm{tmp, false}
case arg_msbw:
tmp := (x >> 16) & ((1 << 5) - 1)
return Uimm{tmp, false}
case arg_lsbd:
tmp := (x >> 10) & ((1 << 6) - 1)
return Uimm{tmp, false}
case arg_msbd:
tmp := (x >> 16) & ((1 << 6) - 1)
return Uimm{tmp, false}
case arg_hint_4_0:
tmp := x & ((1 << 5) - 1)
return Uimm{tmp, false}
case arg_hint_14_0:
tmp := x & ((1 << 15) - 1)
return Uimm{tmp, false}
case arg_level_14_0:
tmp := x & ((1 << 15) - 1)
return Uimm{tmp, false}
case arg_level_17_10:
tmp := (x >> 10) & ((1 << 8) - 1)
return Uimm{tmp, false}
case arg_seq_17_10:
tmp := (x >> 10) & ((1 << 8) - 1)
return Uimm{tmp, false}
case arg_si12_21_10:
var tmp int16
// no int12, so sign-extend a 12-bit signed to 16-bit signed
if (x & 0x200000) == 0x200000 {
tmp = int16(((x >> 10) & ((1 << 12) - 1)) | 0xf000)
} else {
tmp = int16(((x >> 10) & ((1 << 12) - 1)) | 0x0000)
}
return Simm16{tmp, 12}
case arg_si14_23_10:
var tmp int32
if (x & 0x800000) == 0x800000 {
tmp = int32((((x >> 10) & ((1 << 14) - 1)) << 2) | 0xffff0000)
} else {
tmp = int32((((x >> 10) & ((1 << 14) - 1)) << 2) | 0x00000000)
}
return Simm32{tmp, 14}
case arg_si16_25_10:
var tmp int32
if (x & 0x2000000) == 0x2000000 {
tmp = int32(((x >> 10) & ((1 << 16) - 1)) | 0xffff0000)
} else {
tmp = int32(((x >> 10) & ((1 << 16) - 1)) | 0x00000000)
}
return Simm32{tmp, 16}
case arg_si20_24_5:
var tmp int32
if (x & 0x1000000) == 0x1000000 {
tmp = int32(((x >> 5) & ((1 << 20) - 1)) | 0xfff00000)
} else {
tmp = int32(((x >> 5) & ((1 << 20) - 1)) | 0x00000000)
}
return Simm32{tmp, 20}
case arg_offset_20_0:
var tmp int32
if (x & 0x10) == 0x10 {
tmp = int32(((((x << 16) | ((x >> 10) & ((1 << 16) - 1))) & ((1 << 21) - 1)) << 2) | 0xff800000)
} else {
tmp = int32((((x << 16) | ((x >> 10) & ((1 << 16) - 1))) & ((1 << 21) - 1)) << 2)
}
return OffsetSimm{tmp, 21}
case arg_offset_15_0:
var tmp int32
if (x & 0x2000000) == 0x2000000 {
tmp = int32((((x >> 10) & ((1 << 16) - 1)) << 2) | 0xfffc0000)
} else {
tmp = int32((((x >> 10) & ((1 << 16) - 1)) << 2) | 0x00000000)
}
return OffsetSimm{tmp, 16}
case arg_offset_25_0:
var tmp int32
if (x & 0x200) == 0x200 {
tmp = int32(((((x << 16) | ((x >> 10) & ((1 << 16) - 1))) & ((1 << 26) - 1)) << 2) | 0xf0000000)
} else {
tmp = int32(((((x << 16) | ((x >> 10) & ((1 << 16) - 1))) & ((1 << 26) - 1)) << 2) | 0x00000000)
}
return OffsetSimm{tmp, 26}
default:
return nil
}
}