feat(asm): add RISC-V ELF relocatable object emission and SB relocation
support
This commit is contained in:
+177
-4
@@ -29,7 +29,7 @@ func firstTextRISCV(t *testing.T, src string) *ast.Text {
|
||||
// assembleRISCVHelper assembles one TEXT function and returns its code bytes.
|
||||
func assembleRISCVHelper(t *testing.T, fn *ast.Text) []byte {
|
||||
t.Helper()
|
||||
code, _, err := assembleRISCV(fn)
|
||||
code, _, _, err := assembleRISCV(fn)
|
||||
if err != nil {
|
||||
t.Fatalf("assemble: %v", err)
|
||||
}
|
||||
@@ -179,11 +179,11 @@ TEXT ·frame(SB), NOSPLIT, $0-8
|
||||
}
|
||||
|
||||
func TestRISCV_RVC_loadStore(t *testing.T) {
|
||||
// Verify that loads/stores from SP (X2) are compressed.
|
||||
// Verify that loads/stores from SP are compressed.
|
||||
fn := firstTextRISCV(t, `#include "textflag.h"
|
||||
TEXT ·rvcstore(SB), NOSPLIT, $0
|
||||
LD 0(X2), X10
|
||||
SD X10, 8(X2)
|
||||
LD 0(SP), X10
|
||||
SD X10, 8(SP)
|
||||
RET
|
||||
`)
|
||||
code := assembleRISCVHelper(t, fn)
|
||||
@@ -574,3 +574,176 @@ TEXT ·cfsdsp(SB), NOSPLIT, $0-8
|
||||
t.Errorf("expected 4 bytes with C.FSDSP, got %d", len(code))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRISCV_SB_addr(t *testing.T) {
|
||||
// MOV $sym<>(SB), rd → AUIPC + ADDI (8 bytes for SB).
|
||||
src := `#include "textflag.h"
|
||||
TEXT ·sbaddr(SB), NOSPLIT, $0
|
||||
MOV $answer<>(SB), X10
|
||||
RET
|
||||
GLOBL answer<>(SB), RODATA, $8
|
||||
DATA answer<>+0(SB)/8, $42
|
||||
`
|
||||
f, errs := parser.Parse("t_riscv64.s", src)
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("parse: %v", errs)
|
||||
}
|
||||
img, err := AssembleFileRISCV(f)
|
||||
if err != nil {
|
||||
t.Fatalf("AssembleFileRISCV: %v", err)
|
||||
}
|
||||
// AUIPC(4) + ADDI(4) + C.JR(2) = 10
|
||||
if img.Funcs[0].Size != 10 {
|
||||
t.Errorf("expected 10 bytes, got %d", img.Funcs[0].Size)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRISCV_SB_store(t *testing.T) {
|
||||
// MOV rd, sym<>(SB) → AUIPC + SD (8 bytes for SB).
|
||||
src := `#include "textflag.h"
|
||||
TEXT ·sbstore(SB), NOSPLIT, $0
|
||||
MOV X10, result<>(SB)
|
||||
RET
|
||||
GLOBL result<>(SB), NOPTR, $8
|
||||
`
|
||||
f, errs := parser.Parse("t_riscv64.s", src)
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("parse: %v", errs)
|
||||
}
|
||||
img, err := AssembleFileRISCV(f)
|
||||
if err != nil {
|
||||
t.Fatalf("AssembleFileRISCV: %v", err)
|
||||
}
|
||||
// AUIPC X31(4) + SD X10,0(X31)(4) + C.JR(2) = 10
|
||||
if img.Funcs[0].Size != 10 {
|
||||
t.Errorf("expected 10 bytes, got %d", img.Funcs[0].Size)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRISCV_ELF(t *testing.T) {
|
||||
src := `#include "textflag.h"
|
||||
TEXT ·simple(SB), NOSPLIT, $0
|
||||
RET
|
||||
`
|
||||
f, errs := parser.Parse("t_riscv64.s", src)
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("parse: %v", errs)
|
||||
}
|
||||
img, err := AssembleFileRISCV(f)
|
||||
if err != nil {
|
||||
t.Fatalf("AssembleFileRISCV: %v", err)
|
||||
}
|
||||
obj, err := img.ELFRISCVObject()
|
||||
if err != nil {
|
||||
t.Fatalf("ELFRISCVObject: %v", err)
|
||||
}
|
||||
if len(obj) < 4 || obj[0] != 0x7f || obj[1] != 'E' || obj[2] != 'L' || obj[3] != 'F' {
|
||||
t.Fatal("not a valid ELF file")
|
||||
}
|
||||
if len(obj) >= 20 {
|
||||
machine := uint16(obj[18]) | uint16(obj[19])<<8
|
||||
if machine != 243 {
|
||||
t.Errorf("e_machine = %d, want 243 (EM_RISCV)", machine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRISCV_ELF_withData(t *testing.T) {
|
||||
src := `#include "textflag.h"
|
||||
TEXT ·get(SB), NOSPLIT, $0
|
||||
RET
|
||||
GLOBL val<>(SB), RODATA, $4
|
||||
DATA val<>+0(SB)/4, $7
|
||||
`
|
||||
f, errs := parser.Parse("t_riscv64.s", src)
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("parse: %v", errs)
|
||||
}
|
||||
img, err := AssembleFileRISCV(f)
|
||||
if err != nil {
|
||||
t.Fatalf("AssembleFileRISCV: %v", err)
|
||||
}
|
||||
if len(img.DataSyms) != 1 {
|
||||
t.Fatalf("expected 1 data symbol, got %d", len(img.DataSyms))
|
||||
}
|
||||
if img.DataSyms[0].Name != "val" {
|
||||
t.Errorf("data symbol name = %q, want val", img.DataSyms[0].Name)
|
||||
}
|
||||
if img.DataSyms[0].Size != 4 {
|
||||
t.Errorf("data symbol size = %d, want 4", img.DataSyms[0].Size)
|
||||
}
|
||||
obj, err := img.ELFRISCVObject()
|
||||
if err != nil {
|
||||
t.Fatalf("ELFRISCVObject: %v", err)
|
||||
}
|
||||
_ = obj
|
||||
}
|
||||
|
||||
func TestRISCV_SB_load(t *testing.T) {
|
||||
// MOV sym<>(SB), rd → AUIPC + LD (8 bytes for SB).
|
||||
src := `#include "textflag.h"
|
||||
TEXT ·sbload(SB), NOSPLIT, $0
|
||||
MOV answer<>(SB), X10
|
||||
RET
|
||||
GLOBL answer<>(SB), RODATA, $8
|
||||
DATA answer<>+0(SB)/8, $42
|
||||
`
|
||||
f, errs := parser.Parse("t_riscv64.s", src)
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("parse: %v", errs)
|
||||
}
|
||||
img, err := AssembleFileRISCV(f)
|
||||
if err != nil {
|
||||
t.Fatalf("AssembleFileRISCV: %v", err)
|
||||
}
|
||||
// AUIPC(4) + LD(4) + C.JR(2) = 10
|
||||
if img.Funcs[0].Size != 10 {
|
||||
t.Errorf("expected 10 bytes, got %d", img.Funcs[0].Size)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRISCV_system_instrs(t *testing.T) {
|
||||
// Test FENCE, ECALL, EBREAK encoding.
|
||||
fn := firstTextRISCV(t, `#include "textflag.h"
|
||||
TEXT ·sys(SB), NOSPLIT, $0
|
||||
FENCE
|
||||
ECALL
|
||||
EBREAK
|
||||
RET
|
||||
`)
|
||||
code := assembleRISCVHelper(t, fn)
|
||||
// 3 system instructions × 4 bytes + C.JR(2) = 14
|
||||
if len(code) != 14 {
|
||||
t.Errorf("expected 14 bytes, got %d (% x)", len(code), code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRISCV_MOV_sym_FP_error(t *testing.T) {
|
||||
// MOV $sym(FP), rd should return an error (unsupported).
|
||||
fn := firstTextRISCV(t, `#include "textflag.h"
|
||||
TEXT ·badfp(SB), NOSPLIT, $0
|
||||
MOV $arg(FP), X10
|
||||
RET
|
||||
`)
|
||||
_, _, _, err := assembleRISCV(fn)
|
||||
if err == nil {
|
||||
t.Error("expected error for MOV $arg(FP), got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRISCV_CALL(t *testing.T) {
|
||||
// CALL target → AUIPC + JALR (8 bytes).
|
||||
fn := firstTextRISCV(t, `#include "textflag.h"
|
||||
TEXT ·calltest(SB), NOSPLIT, $0
|
||||
CALL sub
|
||||
done:
|
||||
RET
|
||||
sub:
|
||||
RET
|
||||
`)
|
||||
code := assembleRISCVHelper(t, fn)
|
||||
// CALL(8) + C.JR(2) + C.JR(2) = 12
|
||||
if len(code) != 12 {
|
||||
t.Errorf("expected 12 bytes with CALL, got %d", len(code))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user