| // 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 |
| } |
| } |