简体   繁体   中英

How to use C.ADDI4SPN and C.ADDI16SP instructions (compressed subset) of RISC-V architecture?

I can't figure out how to call these two instructions in a proper way. The first operand of the first instruction C.ADDI4SPN should be a register, and the second one, if I'm right, should be a number which is scaled by 4.

But when I try to call the instruction I get the message that the operands are illegal.

The same thing is with the second instruction C.ADDI16SP , the only difference is that the number should be scaled by 16. These are the descriptions of the instructions in the manual:

C.ADDI16SP adds the non-zero sign-extended 6-bit immediate to the value in the stack pointer (sp=x2), where the immediate is scaled to represent multiples of 16 in the range (-512,496).

C.ADDI4SPN is a CIW-format RV32C/RV64C-only instruction that adds a zero-extended non-zero immediate, scaled by 4, to the stack pointer, x2, and writes the result to rd0

These are the examples of how I attempt to use the instructions:

c.addi16sp 32
c.addi4spn x10, 8

Start by going backward (and understand that assembly is tool specific not target).

.hword 0x110c

   0:   110c                    addi    x11,x2,160

Then try some more

.hword 0x110c
addi    x11,x2,160
addi    x12,x2,160
addi    x13,x2,160
addi    x14,x2,160
addi    x14,x2,40
addi    x14,x2,4

00000000 <.text>:
   0:   110c                    addi    x11,x2,160
   2:   110c                    addi    x11,x2,160
   4:   1110                    addi    x12,x2,160
   6:   1114                    addi    x13,x2,160
   8:   1118                    addi    x14,x2,160
   a:   1038                    addi    x14,x2,40
   c:   0058                    addi    x14,x2,4

This instruction is used to generate pointers to stack-allocated variables, and expands to addi rd ′ , x2, nzuimm.

This was done with gnu assembler, and objdump from binutils.

It appears you need an immediate that is a multiple of four:

addi    x14,x2,4
addi    x14,x2,5
addi    x14,x2,6
addi    x14,x2,7
addi    x14,x2,8

00000000 <.text>:
   0:   0058                addi    x14,x2,4
   2:   00510713            addi    x14,x2,5
   6:   00610713            addi    x14,x2,6
   a:   00710713            addi    x14,x2,7
   e:   0038                addi    x14,x2,8

to get it to optimize into this instruction.

It seems that for some reason I don't know, that we have to name the sp register when using these instructions:

c.addi4sp, x10, sp, 8
c.addi16sp sp, 16

It is possible that this is for consistency with the uncompressed instruction expansion, where you also have to name sp .

However, while some might count this as a feature, others (such as myself) more likely to count this as a bug or an oddity at best, because an implicit register (that you can't ever change) probably shouldn't be required in the assembly form — when explicitly using the compressed opcode.

The base instruction set (ie sans compression) has no implicit registers — from the machine code perspective — all operands are specified in the machine instruction.

Certain assembly mnemonics allow omitting the register, which is then filled in by the assembler in generating the machine code: jal and ret (pseudo instructions) for example do not allow or require the assembly program to name a register yet the machine code for these instructions has an rd / rs1 register field (respectively) filled in with x1 / ra by the assembler.

To use c.lwsp , we also specify the sp register, so it looks very much like an lw instruction. And c.jal looks just like the jal pseudo instruction in assuming x1 as the link register — even though c.jal does hard code x1 as an implicit target register, while jal 's translation does not — from the machine code perspective.

So, I guess what they're going for is maximum compatibility with the uncompressed instructions assembly forms. And I guess that makes the disassembly a bit more palatable, since it cannot tell whether you originally used a compressed opcode vs. the assembler compressing the instruction (though I'm not sure how worthwhile it is to show disassembly of compressed instructions using uncompressed but compressible forms).


test.s:

.text
c.addi4spn a0, sp, 8  # compressed instruction, must name sp
addi a0, sp, 8        # non-compressed instruction, gets compressed to c.addi4spn

c.addi16sp sp, 16     # compressed instruction, must name sp
addi sp, sp, 16       # non-compressed instruction, gets compressed to c.addi

c.addi16sp sp, 128    # compressed instruction, must name sp
addi sp, sp, 128      # non-compressed instruction, gets compressed to c.addi16sp

Disassembly of section .text:

00000000 <.text>:
   0:   0028                    addi    a0,sp,8    # c.addi4spn
   2:   0028                    addi    a0,sp,8    # c.addi4spn
   4:   6141                    addi    sp,sp,16   # c.addi16sp
   6:   0141                    addi    sp,sp,16   # c.addi
   8:   6109                    addi    sp,sp,128  # c.addi16sp
   a:   6109                    addi    sp,sp,128  # c.addi16sp

As you can see the disassembler assumes you used (or want to see) non-compressed instruction syntax even though the assembler may or may not have converted these to their compressed equivalents.

As mentioned by @Erik Eidt you have to name the sp register.
This choice was made in riscv-opc.c.
https://github.com/riscv/riscv-binutils-gdb/blob/riscv-binutils-2.35/opcodes/riscv-opc.c
If you take a look you have these two definitions:

{"c.addi4spn", 0, INSN_CLASS_C,   "Ct,Cc,CK", MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN, match_c_addi4spn, 0 },
{"c.addi16sp", 0, INSN_CLASS_C,   "Cc,CL", MATCH_C_ADDI16SP, MASK_C_ADDI16SP, match_c_addi16sp, 0 },

Ct Cc CK and CL are the operands:
Ct : /* RS2 x8-x15 */
Cc : sp ( this is why you need the sp ).
CK and CL for the RVC_ADDI4SPN_IMM and RVC_ADDI16SP_IMM.

The reason why you have addi in the objdump is because :

{"addi",        0, INSN_CLASS_C,   "Ct,Cc,CK", MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN, match_c_addi4spn, INSN_ALIAS },

and

{"addi",        0, INSN_CLASS_C,   "Cc,Cc,CL", MATCH_C_ADDI16SP, MASK_C_ADDI16SP, match_c_addi16sp, INSN_ALIAS },

Since they are declared before the previous ones , the objdump match an addi.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM