fix(asm): materialise RISC-V MOV immediates
This commit is contained in:
+53
-20
@@ -122,7 +122,9 @@ func assembleRISCV(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry, [
|
||||
}
|
||||
|
||||
// riscvInstrSize returns the encoded size in bytes of a RISC-V instruction.
|
||||
// Most instructions are 4 bytes; MOV with a large immediate is 8 (LUI+ADDIW).
|
||||
// Most instructions are 4 bytes; MOV with a large immediate and I-type
|
||||
// arithmetic with a large immediate expand to several (possibly compressed)
|
||||
// instructions.
|
||||
func riscvInstrSize(instr *ast.Instr, fi riscvFrameInfo) int {
|
||||
mnem := instr.Mnemonic.Text
|
||||
ops := instr.Operands
|
||||
@@ -142,12 +144,9 @@ func riscvInstrSize(instr *ast.Instr, fi riscvFrameInfo) int {
|
||||
if isMemOperand(ops[1]) && ops[1].Addr.Sym != nil && ops[1].Addr.Sym.Pseudo == "SB" {
|
||||
return 8
|
||||
}
|
||||
// MOV $imm, rd → large immediate needs LUI+ADDIW.
|
||||
if isImmOperand(ops[0]) {
|
||||
imm := immFromOperand(ops[0])
|
||||
if imm < -2048 || imm > 2047 {
|
||||
return 8
|
||||
}
|
||||
// MOV $imm, rd → size depends on the immediate and RVC compression.
|
||||
if isImmOperand(ops[0]) && ops[0].Imm.Sym == nil {
|
||||
return riscvMovImmSize(regFromOperand(ops[1]), immFromOperand(ops[0]))
|
||||
}
|
||||
}
|
||||
// I-type arithmetic with a large immediate expands to several instructions.
|
||||
@@ -636,28 +635,62 @@ func encodeRISCVMov(instr *ast.Instr, offsets map[string]int, fi riscvFrameInfo,
|
||||
}
|
||||
}
|
||||
|
||||
// encodeRISCVLoadImm encodes loading an immediate into a register.
|
||||
// For 12-bit immediates: ADDI $imm, ZERO, rd.
|
||||
// For larger: LUI $hi, rd + ADDIW $lo, rd, rd.
|
||||
// encodeRISCVLoadImm encodes loading an immediate into a register (MOV $imm,
|
||||
// rd), matching the toolchain's instructionsForMOVConst. For 12-bit
|
||||
// immediates it emits ADDI $imm, ZERO, rd (compressed to C.LI when it fits
|
||||
// six signed bits); for larger immediates it emits LUI + [ADDIW], with the LUI
|
||||
// and ADDIW compressed to C.LUI / C.ADDIW when their immediate fits.
|
||||
func encodeRISCVLoadImm(rd int, imm int32) []byte {
|
||||
if imm >= -2048 && imm <= 2047 {
|
||||
word := riscvIType(riscvEnc{0x13, 0x0, 0x00}, rd, 0, imm)
|
||||
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}
|
||||
if rd != 0 && imm >= -32 && imm <= 31 {
|
||||
return word16(rvcCI(0x2, uint32(rd), uint32(imm)&0x3F)) // C.LI
|
||||
}
|
||||
return wordLE(riscvIType(riscvEnc{0x13, 0x0, 0x00}, rd, 0, imm))
|
||||
}
|
||||
|
||||
// LUI + ADDIW for larger constants.
|
||||
low, high := splitRISCV32Imm(imm)
|
||||
var out []byte
|
||||
hi := int32((uint32(imm)+0x800)>>12) << 12 // LUI loads upper 20 bits
|
||||
lo := imm - hi
|
||||
wordLUI := riscvUType(riscvEnc{0x37, 0x0, 0x00}, rd, hi)
|
||||
out = append(out, byte(wordLUI), byte(wordLUI>>8), byte(wordLUI>>16), byte(wordLUI>>24))
|
||||
if lo != 0 {
|
||||
wordADDIW := riscvIType(riscvEnc{0x1B, 0x0, 0x00}, rd, rd, lo)
|
||||
out = append(out, byte(wordADDIW), byte(wordADDIW>>8), byte(wordADDIW>>16), byte(wordADDIW>>24))
|
||||
if rd != 0 && rd != 2 && high >= -32 && high <= 31 {
|
||||
out = append(out, word16(rvcCI(0x3, uint32(rd), uint32(high)&0x3F))...) // C.LUI
|
||||
} else {
|
||||
out = append(out, wordLE(riscvUType(riscvEnc{0x37, 0x0, 0x00}, rd, high<<12))...)
|
||||
}
|
||||
if low != 0 {
|
||||
if low >= -32 && low <= 31 {
|
||||
out = append(out, word16(rvcCI(0x1, uint32(rd), uint32(low)&0x3F))...) // C.ADDIW
|
||||
} else {
|
||||
out = append(out, wordLE(riscvIType(riscvEnc{0x1B, 0x0, 0x00}, rd, rd, low))...)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// riscvMovImmSize returns the encoded byte length of MOV $imm, rd, mirroring
|
||||
// encodeRISCVLoadImm's expansion and compression.
|
||||
func riscvMovImmSize(rd int, imm int32) int {
|
||||
if imm >= -2048 && imm <= 2047 {
|
||||
if rd != 0 && imm >= -32 && imm <= 31 {
|
||||
return 2 // C.LI
|
||||
}
|
||||
return 4 // ADDI
|
||||
}
|
||||
low, high := splitRISCV32Imm(imm)
|
||||
size := 0
|
||||
if rd != 0 && rd != 2 && high >= -32 && high <= 31 {
|
||||
size += 2 // C.LUI
|
||||
} else {
|
||||
size += 4 // LUI
|
||||
}
|
||||
if low != 0 {
|
||||
if low >= -32 && low <= 31 {
|
||||
size += 2 // C.ADDIW
|
||||
} else {
|
||||
size += 4 // ADDIW
|
||||
}
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// splitRISCV32Imm splits a signed 32-bit immediate into a signed 12-bit low
|
||||
// part and a signed 20-bit high part, mirroring cmd/internal/obj/riscv's
|
||||
// Split32BitImmediate. The high part is returned unshifted; callers place it
|
||||
|
||||
Reference in New Issue
Block a user