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.