Loading

loongarch 基础指令

loongarch基础指令格式

image

R表示寄存器,I表示立即数

寄存器使用约定

  • 整数寄存器
Name Alias Meaning Preserved across calls
$r0 $zero Zero register Constant Zero
$r1 $ra Return Address No
$r2 $tp Thread Local Cache Non-allocatable
$r3 $sp Stack Pointer Yes
$r4-$r11 $a0-$a7 Argument registers No
$r4-$r5 $v0-$v1 Return value No
$r12-$r20 $t0-t8 Temp Registers No
$r21 Reserved Non-allocatable
$r22 $fp/$s9 Frame pointer/Static Register Yes
$r23-$31 $s0-$s8 Static Registers Yes
  • 浮点寄存器
Name Alias Meaning Preserved across calls
$f0-$f7 $fa0-$fa7 Argument registers No
$f0-$f1 $fa0-$fa1 Return Value registers No
$f8-$f23 $ft0-$ft15 Temporary registers No
$f24-$f31 $fs0-$fs7 Static registers Yes

caller-save和callee-save

caller-savecalle-save是相对于编译器abi而言的, 而caller-save是相对于callee-save而言的,所以得先搞懂什么是callee-save函数

callee也就是当前函数,所谓的callee-save寄存器指的是,如果callee在执行过程中修改了该寄存器的值,那么需要将该寄存器的值恢复到原来的值。那么为什么要恢复到原来的值呢?是因为原来的值虽然对于当前的函数是无用的,但是对于整个执行流(可以认为是一个协程,协程不存在的情况下是一个线程,就是函数栈得连续)是有用的,这里修改了别的函数咋办。典型的callee-save就是sp,tp寄存器,剩下的s1-s9都是编译器规定的,没有特别作用。

反过来而言还有一部分寄存器,callee可以随便修改它的值,也就是除过callee-save以及r21, $tp以外剩下的都是caller-save。然后就是了解这些有什么用?

再次强调caller-savecallee-save是编译器决定的,规定这些有什么用:

  1. 编译器在从c语言生成汇编的时候a系列的寄存器用完就只能使用栈空间了,这是编译其生成汇编进行寄存器分配的准则之一。
  2. 用来指导手写汇编,我们在手写汇编.S文件的时候,我们编写的函数要符合寄存器的使用规范,举个例子:你在自己写的汇编里面修改了sp指针的内容但是没有复原,就会导致实际执行的时候caller的sp指针乱掉

boost context

// https://github.com/boostorg/context/blob/develop/src/asm
/*******************************************************
 *                                                     *
 *  -------------------------------------------------  *
 *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *
 *  -------------------------------------------------  *
 *  |     0     |     8     |    16     |     24    |  *
 *  -------------------------------------------------  *
 *  |    FS0    |    FS1    |    FS2    |    FS3    |  *
 *  -------------------------------------------------  *
 *  -------------------------------------------------  *
 *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *
 *  -------------------------------------------------  *
 *  |     32    |    40     |     48    |     56    |  *
 *  -------------------------------------------------  *
 *  |    FS4    |    FS5    |    FS6    |    FS7    |  *
 *  -------------------------------------------------  *
 *  -------------------------------------------------  *
 *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *
 *  -------------------------------------------------  *
 *  |     64    |    72     |     80    |     88    |  *
 *  -------------------------------------------------  *
 *  |    S0     |    S1     |     S2    |     S3    |  *
 *  -------------------------------------------------  *
 *  -------------------------------------------------  *
 *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *
 *  -------------------------------------------------  *
 *  |  96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 |  *
 *  -------------------------------------------------  *
 *  |    S4     |    S5     |     S6    |     S7    |  *
 *  -------------------------------------------------  *
 *  -------------------------------------------------  *
 *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *
 *  -------------------------------------------------  *
 *  | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 |  *
 *  -------------------------------------------------  *
 *  |    S8     |    FP     |     RA    |     PC    |  *
 *  -------------------------------------------------  *
 *                                                     *
 * *****************************************************/

.file "jump_loongarch64_sysv_elf_gas.S"
.text
.globl jump_fcontext
.align 2
.type jump_fcontext,@function
jump_fcontext:
    # reserve space on stack
    addi.d  $sp, $sp, -160

    # save fs0 - fs7
    fst.d  $fs0, $sp, 0
    fst.d  $fs1, $sp, 8
    fst.d  $fs2, $sp, 16
    fst.d  $fs3, $sp, 24
    fst.d  $fs4, $sp, 32
    fst.d  $fs5, $sp, 40
    fst.d  $fs6, $sp, 48
    fst.d  $fs7, $sp, 56

    # save s0 - s8, fp, ra
    st.d  $s0, $sp, 64
    st.d  $s1, $sp, 72
    st.d  $s2, $sp, 80
    st.d  $s3, $sp, 88
    st.d  $s4, $sp, 96
    st.d  $s5, $sp, 104
    st.d  $s6, $sp, 112
    st.d  $s7, $sp, 120
    st.d  $s8, $sp, 128
    st.d  $fp, $sp, 136
    st.d  $ra, $sp, 144

    # save RA as PC
    st.d  $ra, $sp, 152

    # store SP (pointing to context-data) in A2
    move  $a2, $sp

    # restore SP (pointing to context-data) from A0
    move  $sp, $a0

    # load fs0 - fs7
    fld.d  $fs0, $sp, 0
    fld.d  $fs1, $sp, 8
    fld.d  $fs2, $sp, 16
    fld.d  $fs3, $sp, 24
    fld.d  $fs4, $sp, 32
    fld.d  $fs5, $sp, 40
    fld.d  $fs6, $sp, 48
    fld.d  $fs7, $sp, 56

    #load s0 - s7
    ld.d  $s0, $sp, 64
    ld.d  $s1, $sp, 72
    ld.d  $s2, $sp, 80
    ld.d  $s3, $sp, 88
    ld.d  $s4, $sp, 96
    ld.d  $s5, $sp, 104
    ld.d  $s6, $sp, 112
    ld.d  $s7, $sp, 120
    ld.d  $s8, $sp, 128
    ld.d  $fp, $sp, 136
    ld.d  $ra, $sp, 144

    # return transfer_t from jump
    # pass transfer_t as first arg in context function
    # a0 == FCTX, a1 == DATA
    move  $a0, $a2

    # load PC
    ld.d  $a2, $sp, 152

    # restore stack
    addi.d  $sp, $sp, 160

    # jump to context
    jr  $a2
.size jump_fcontext, .-jump_fcontext

/* Mark that we don't need executable stack.  */
.section .note.GNU-stack,"",%progbits
  1. 这段代码是boost-context中进行上下问切换的代码,上面的图是寄存器保存在栈当中的内存视图。
  2. 这里主要保存的就是callee-saved寄存器,也就是static registersra

普通访存类指令

ld 指令

ld 指令的格式如下:

ld.{b/h/w/d}{u} rd, rj, si12

这里为64位系统,这里b/h/w/d分别表示1/2/4/8个字节,si12表示 ld.w指令执行的伪代码如下

# grlen = 64
# word_len = 32
vaddr = rj + signed_extend(si12, grlen)
# 内存对其检查
mem_compliance_check(vaddr)
paddr = addr_translate(vaddr)
word = mem_load(paddr, word_len)
rd = signed_extend(word, grlen)

ld.wu指令执行的伪代码如下:

vaddr = rj + sign_extend(si12, grlen)
mem_compliance_check(vaddr)
paddr = addr_translate(vaddr)
word = mem_load(paddr, word_len)
rd = zero_extend(word, grlen)

st 指令

ld 指令的格式如下:

st.{b/h/w/d} rd, rj, si12

st.w指令执行的伪代码如下

vaddr = rj + sign_extend(si12, grlen)
mem_compliance_check(vaddr)
paddr = addr_translate(vaddr)
// 0到31位
mem_store(rd[31:0], paddr, word_len)

ldptr

ldptr的指令格式如下

ldptr.{w/d} rd, rj, si14

// 这里是算术左移动
vaddr = rj + sign_extend(si14<<2, grlen)
mem_compliance_check(vaddr)
paddr = addr_translate(vaddr)
word = mem_load(paddr, word_len)
rd = sign_extend(word, grlen)

stptr

stptr.{w/d} rd, rj, si14

vaddr = rj + sign_extend(si14<<2, grlen)
mem_compliance_check(vaddr)
paddr = addr_translate(vaddr)
// 0到31位
mem_store(rd[31:0], paddr, word_len)

跳转指令

B, BEQ,BNE,BLT[U],BGE[U],BEQZ,BNEZ

Name Format Meaning
b b offs26 pc += sign_extend(offs26<<2)
beq beq rj, rd, offs16 if rj==rd:<br> pc += sign_extend(offs16<<2)
bneq bneq rj, rd, offs16 if rj!=rd: pc += sign_extend(offs16<<2)
blt blt rj, rd, offs16 if rj<rd: pc += sign_extend(offs16<<2)
bge bge rj, rd, offs16 if rj>=rd: pc += sign_extend(offs16<<2)
bltu bltu rj, rd, offs16 if unsign(rj)<unsign(rd): pc += sign_extend(offs16<<2)
bgeu bgeu rj, rd, offs16 if unsign(rj)>=unsign(rd): pc += sign_extend(offs16<<2)
beqz beqz rj, offs21 if rj==0: pc += sign_extend(offs21<<2)
bnez bne rj, offs21 if rj!=0: pc += sign_extend(offs21<<2)

BL, JIRL

Name Format Meaning
bl bl offs26 ra = pc+4; pc += sign_extend(offs26<<2)
jirl jirl rd, rj, offs16 rd = pc+4; pc = rj +sign_extend(offs26<<2)

杂项指令

nop指令

nop是一条伪指令,它的实现如下:

andi $r0,$r0,0x0

rdtime

rdtime.d rd, rj

龙芯指令系统定义了一个恒定频率的计时器,其主体是一个64位的计数器,称为Stable CounterStable Counter复位后置为0, 随后每个计数时钟周期自增1,当计数至全部为1的时候,自动绕回到0,继续自增。同时每个计时器有一个全局可配置的唯一编号Counter ID

对于rdtime.d而言Stable Counter的值写入rd,Counter ID的值写入rj。

使用如下:

unsigned long result;
asm volatile ("rdtime.d %0,$r0" : "=r" (result));
return result;

syscall

syscall code

目前这个code是没有什么意义的,一般默认为0即可

// https://github.com/loongson-community/musl/blob/master/arch/loongarch64/syscall_arch.h#L99
static inline long __syscall6(long n, long a, long b, long c, long d, long e, long f)
{
	register long a0 __asm__("$a0") = a;
	register long a1 __asm__("$a1") = b;
	register long a2 __asm__("$a2") = c;
	register long a3 __asm__("$a3") = d;
	register long a4 __asm__("$a4") = e;
	register long a5 __asm__("$a5") = f;
	register long a7 __asm__("$a7") = n;

	__asm__ __volatile__ (
		"syscall 0"
		: "+&r"(a0)
	        : "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5)
		: SYSCALL_CLOBBERLIST);
	return a0;
}
posted @ 2023-08-17 17:48  Mogul_Kahn  阅读(870)  评论(0)    收藏  举报